Bug 1460244 - Tables with CSS display properties no longer participate in layout table calculation, r=marcoz

This commit is contained in:
Alexander Surkov 2018-05-25 10:55:52 -04:00
Родитель 2cf03f5d2f
Коммит 246756a336
8 изменённых файлов: 334 добавлений и 290 удалений

Просмотреть файл

@ -40,6 +40,21 @@ ARIAGridAccessible::NativeRole() const
return r != roles::NOTHING ? r : roles::TABLE;
}
already_AddRefed<nsIPersistentProperties>
ARIAGridAccessible::NativeAttributes()
{
nsCOMPtr<nsIPersistentProperties> attributes =
AccessibleWrap::NativeAttributes();
if (IsProbablyLayoutTable()) {
nsAutoString unused;
attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"),
NS_LITERAL_STRING("true"), unused);
}
return attributes.forget();
}
////////////////////////////////////////////////////////////////////////////////
// Table

Просмотреть файл

@ -26,6 +26,7 @@ public:
// Accessible
virtual a11y::role NativeRole() const override;
virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
virtual TableAccessible* AsTable() override { return this; }
// TableAccessible

Просмотреть файл

@ -0,0 +1,240 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TableAccessible.h"
#include "Accessible-inl.h"
#include "nsTableCellFrame.h"
#include "nsTableWrapperFrame.h"
using namespace mozilla;
using namespace mozilla::a11y;
bool
TableAccessible::IsProbablyLayoutTable()
{
// Implement a heuristic to determine if table is most likely used for layout.
// XXX do we want to look for rowspan or colspan, especialy that span all but
// a couple cells at the beginning or end of a row/col, and especially when
// they occur at the edge of a table?
// XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
// This will allow release trunk builds to be used by testers to refine
// the algorithm. Integrate it into Logging.
// Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
#ifdef SHOW_LAYOUT_HEURISTIC
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
{ \
mLayoutHeuristic = isLayout ? \
NS_LITERAL_STRING("layout table: " heuristic) : \
NS_LITERAL_STRING("data table: " heuristic); \
return isLayout; \
}
#else
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; }
#endif
Accessible* thisacc = AsAccessible();
// Need to see all elements while document is being edited.
if (thisacc->Document()->State() & states::EDITABLE) {
RETURN_LAYOUT_ANSWER(false, "In editable document");
}
// Check to see if an ARIA role overrides the role from native markup,
// but for which we still expose table semantics (treegrid, for example).
if (thisacc->HasARIARole()) {
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
}
dom::Element* el = thisacc->Elm();
if (el->IsMathMLElement(nsGkAtoms::mtable_)) {
RETURN_LAYOUT_ANSWER(false, "MathML matrix");
}
MOZ_ASSERT(el->IsHTMLElement(nsGkAtoms::table),
"Table should not be built by CSS display:table style");
// Check if datatable attribute has "0" value.
if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
NS_LITERAL_STRING("0"), eCaseMatters)) {
RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
}
// Check for legitimate data table attributes.
nsAutoString summary;
if (el->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
!summary.IsEmpty()) {
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
}
// Check for legitimate data table elements.
Accessible* caption = thisacc->FirstChild();
if (caption && caption->IsHTMLCaption() && caption->HasChildren()) {
RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
}
for (nsIContent* childElm = el->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (!childElm->IsHTMLElement())
continue;
if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
nsGkAtoms::colgroup,
nsGkAtoms::tfoot,
nsGkAtoms::thead)) {
RETURN_LAYOUT_ANSWER(false,
"Has col, colgroup, tfoot or thead -- legitimate table structures");
}
if (childElm->IsHTMLElement(nsGkAtoms::tbody)) {
for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
rowElm = rowElm->GetNextSibling()) {
if (rowElm->IsHTMLElement(nsGkAtoms::tr)) {
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
cellElm = cellElm->GetNextSibling()) {
if (cellElm->IsHTMLElement()) {
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
RETURN_LAYOUT_ANSWER(false,
"Has th -- legitimate table structures");
}
if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
RETURN_LAYOUT_ANSWER(false,
"Has headers, scope, or abbr attribute -- legitimate table structures");
}
Accessible* cell = thisacc->Document()->GetAccessible(cellElm);
if (cell && cell->ChildCount() == 1 &&
cell->FirstChild()->IsAbbreviation()) {
RETURN_LAYOUT_ANSWER(false,
"has abbr -- legitimate table structures");
}
}
}
}
}
}
}
// Check for nested tables.
nsCOMPtr<nsIHTMLCollection> nestedTables =
el->GetElementsByTagName(NS_LITERAL_STRING("table"));
if (nestedTables->Length() > 0) {
RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
}
// If only 1 column or only 1 row, it's for layout.
auto colCount = ColCount();
if (colCount <= 1) {
RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
}
auto rowCount = RowCount();
if (rowCount <=1) {
RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
}
// Check for many columns.
if (colCount >= 5) {
RETURN_LAYOUT_ANSWER(false, ">=5 columns");
}
// Now we know there are 2-4 columns and 2 or more rows. Check to see if
// there are visible borders on the cells.
// XXX currently, we just check the first cell -- do we really need to do more?
nsTableWrapperFrame* tableFrame = do_QueryFrame(el->GetPrimaryFrame());
if (!tableFrame) {
RETURN_LAYOUT_ANSWER(false, "table with no frame!");
}
nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
if (!cellFrame) {
RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
}
nsMargin border;
cellFrame->GetXULBorder(border);
if (border.top && border.bottom && border.left && border.right) {
RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
}
// Rules for non-bordered tables with 2-4 columns and 2+ rows from here on
// forward.
// Check for styled background color across rows (alternating background
// color is a common feature for data tables).
auto childCount = thisacc->ChildCount();
nscolor rowColor = 0;
nscolor prevRowColor;
for (auto childIdx = 0U; childIdx < childCount; childIdx++) {
Accessible* child = thisacc->GetChildAt(childIdx);
if (child->IsHTMLTableRow()) {
prevRowColor = rowColor;
nsIFrame* rowFrame = child->GetFrame();
MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up");
if (!rowFrame) {
RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy");
}
rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame);
if (childIdx > 0 && prevRowColor != rowColor) {
RETURN_LAYOUT_ANSWER(
false, "2 styles of row background color, non-bordered"
);
}
}
}
// Check for many rows.
const uint32_t kMaxLayoutRows = 20;
if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data
RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
}
// Check for very wide table.
nsIFrame* documentFrame = thisacc->Document()->GetFrame();
nsSize documentSize = documentFrame->GetSize();
if (documentSize.width > 0) {
nsSize tableSize = thisacc->GetFrame()->GetSize();
int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
if (percentageOfDocWidth > 95) {
// 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
// Probably for layout
RETURN_LAYOUT_ANSWER(
true, "<= 4 columns, table width is 95% of document width"
);
}
}
// Two column rules.
if (rowCount * colCount <= 10) {
RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
}
static const nsLiteralString tags[] = {
NS_LITERAL_STRING("embed"),
NS_LITERAL_STRING("object"),
NS_LITERAL_STRING("iframe")
};
for (auto& tag : tags) {
nsCOMPtr<nsIHTMLCollection> descendants = el->GetElementsByTagName(tag);
if (descendants->Length() > 0) {
RETURN_LAYOUT_ANSWER(
true, "Has no borders, and has iframe, object or embed, typical of advertisements"
);
}
}
RETURN_LAYOUT_ANSWER(
false, "No layout factor strong enough, so will guess data"
);
}

Просмотреть файл

@ -173,7 +173,7 @@ public:
/**
* Return true if the table is probably for layout.
*/
virtual bool IsProbablyLayoutTable() { return false; }
virtual bool IsProbablyLayoutTable();
/**
* Convert the table to an Accessible*.

Просмотреть файл

@ -21,6 +21,7 @@ UNIFIED_SOURCES += [
'ImageAccessible.cpp',
'OuterDocAccessible.cpp',
'RootAccessible.cpp',
'TableAccessible.cpp',
'TableCellAccessible.cpp',
'TextLeafAccessible.cpp',
]

Просмотреть файл

@ -860,244 +860,6 @@ HTMLTableAccessible::Description(nsString& aDescription)
#endif
}
bool
HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
{
nsCOMPtr<nsIHTMLCollection> elements =
mContent->AsElement()->GetElementsByTagName(aTagName);
Element* foundItem = elements->Item(0);
if (!foundItem)
return false;
if (aAllowEmpty)
return true;
// Make sure that the item we found has contents and either has multiple
// children or the found item is not a whitespace-only text node.
if (foundItem->GetChildCount() > 1)
return true; // Treat multiple child nodes as non-empty
nsIContent *innerItemContent = foundItem->GetFirstChild();
if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
return true;
// If we found more than one node then return true not depending on
// aAllowEmpty flag.
// XXX it might be dummy but bug 501375 where we changed this addresses
// performance problems only. Note, currently 'aAllowEmpty' flag is used for
// caption element only. On another hand we create accessible object for
// the first entry of caption element (see
// HTMLTableAccessible::InsertChildAt).
return !!elements->Item(1);
}
bool
HTMLTableAccessible::IsProbablyLayoutTable()
{
// Implement a heuristic to determine if table is most likely used for layout
// XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells
// at the beginning or end of a row/col, and especially when they occur at the edge of a table?
// XXX expose this info via object attributes to AT-SPI
// XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
// This will allow release trunk builds to be used by testers to refine the algorithm
// Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
#ifdef SHOW_LAYOUT_HEURISTIC
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
{ \
mLayoutHeuristic = isLayout ? \
NS_LITERAL_STRING("layout table: " heuristic) : \
NS_LITERAL_STRING("data table: " heuristic); \
return isLayout; \
}
#else
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; }
#endif
DocAccessible* docAccessible = Document();
if (docAccessible) {
uint64_t docState = docAccessible->State();
if (docState & states::EDITABLE) { // Need to see all elements while document is being edited
RETURN_LAYOUT_ANSWER(false, "In editable document");
}
}
// Check to see if an ARIA role overrides the role from native markup,
// but for which we still expose table semantics (treegrid, for example).
if (Role() != roles::TABLE)
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
// Role attribute is present, but overridden roles have already been dealt with.
// Only landmarks and other roles that don't override the role from native
// markup are left to deal with here.
RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
}
NS_ASSERTION(mContent->IsHTMLElement(nsGkAtoms::table),
"table should not be built by CSS display:table style");
// Check if datatable attribute has "0" value.
if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
NS_LITERAL_STRING("0"), eCaseMatters)) {
RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
}
// Check for legitimate data table attributes.
nsAutoString summary;
if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
!summary.IsEmpty())
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
// Check for legitimate data table elements.
Accessible* caption = FirstChild();
if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (!childElm->IsHTMLElement())
continue;
if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
nsGkAtoms::colgroup,
nsGkAtoms::tfoot,
nsGkAtoms::thead)) {
RETURN_LAYOUT_ANSWER(false,
"Has col, colgroup, tfoot or thead -- legitimate table structures");
}
if (childElm->IsHTMLElement(nsGkAtoms::tbody)) {
for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
rowElm = rowElm->GetNextSibling()) {
if (rowElm->IsHTMLElement(nsGkAtoms::tr)) {
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
cellElm = cellElm->GetNextSibling()) {
if (cellElm->IsHTMLElement()) {
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
RETURN_LAYOUT_ANSWER(false,
"Has th -- legitimate table structures");
}
if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
RETURN_LAYOUT_ANSWER(false,
"Has headers, scope, or abbr attribute -- legitimate table structures");
}
Accessible* cell = mDoc->GetAccessible(cellElm);
if (cell && cell->ChildCount() == 1 &&
cell->FirstChild()->IsAbbreviation()) {
RETURN_LAYOUT_ANSWER(false,
"has abbr -- legitimate table structures");
}
}
}
}
}
}
}
if (HasDescendant(NS_LITERAL_STRING("table"))) {
RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
}
// If only 1 column or only 1 row, it's for layout
uint32_t colCount = ColCount();
if (colCount <=1) {
RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
}
uint32_t rowCount = RowCount();
if (rowCount <=1) {
RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
}
// Check for many columns
if (colCount >= 5) {
RETURN_LAYOUT_ANSWER(false, ">=5 columns");
}
// Now we know there are 2-4 columns and 2 or more rows
// Check to see if there are visible borders on the cells
// XXX currently, we just check the first cell -- do we really need to do more?
nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
if (!tableFrame)
RETURN_LAYOUT_ANSWER(false, "table with no frame!");
nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
if (!cellFrame)
RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
nsMargin border;
cellFrame->GetXULBorder(border);
if (border.top && border.bottom && border.left && border.right) {
RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
}
/**
* Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
*/
// Check for styled background color across rows (alternating background
// color is a common feature for data tables).
uint32_t childCount = ChildCount();
nscolor rowColor = 0;
nscolor prevRowColor;
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
Accessible* child = GetChildAt(childIdx);
if (child->Role() == roles::ROW) {
prevRowColor = rowColor;
nsIFrame* rowFrame = child->GetFrame();
MOZ_ASSERT(rowFrame, "Table hierarchy got screwed up");
if (!rowFrame) {
RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy");
}
rowColor = rowFrame->StyleBackground()->BackgroundColor(rowFrame);
if (childIdx > 0 && prevRowColor != rowColor)
RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
}
}
// Check for many rows
const uint32_t kMaxLayoutRows = 20;
if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data
RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
}
// Check for very wide table.
nsIFrame* documentFrame = Document()->GetFrame();
nsSize documentSize = documentFrame->GetSize();
if (documentSize.width > 0) {
nsSize tableSize = GetFrame()->GetSize();
int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
if (percentageOfDocWidth > 95) {
// 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
// Probably for layout
RETURN_LAYOUT_ANSWER(true,
"<= 4 columns, table width is 95% of document width");
}
}
// Two column rules
if (rowCount * colCount <= 10) {
RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
}
if (HasDescendant(NS_LITERAL_STRING("embed")) ||
HasDescendant(NS_LITERAL_STRING("object")) ||
HasDescendant(NS_LITERAL_STRING("iframe"))) {
RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, or iframe, typical of advertisements");
}
RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
}
////////////////////////////////////////////////////////////////////////////////
// HTMLCaptionAccessible
////////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -156,7 +156,6 @@ public:
virtual void SelectRow(uint32_t aRowIdx) override;
virtual void UnselectCol(uint32_t aColIdx) override;
virtual void UnselectRow(uint32_t aRowIdx) override;
virtual bool IsProbablyLayoutTable() override;
virtual Accessible* AsAccessible() override { return this; }
// Accessible
@ -199,15 +198,6 @@ protected:
TableSelection aTarget,
bool aIsOuter);
/**
* Return true if table has an element with the given tag name.
*
* @param aTagName [in] tag name of searched element
* @param aAllowEmpty [in, optional] points if found element can be empty
* or contain whitespace text only.
*/
bool HasDescendant(const nsAString& aTagName, bool aAllowEmpty = true);
#ifdef SHOW_LAYOUT_HEURISTIC
nsString mLayoutHeuristic;
#endif

Просмотреть файл

@ -14,106 +14,114 @@
src="../attributes.js"></script>
<script type="application/javascript">
function doTest() {
// Attribute we're looking for
var attr = {
"layout-guess": "true"
};
function isLayoutTable(id) {
testAttrs(id, { "layout-guess": "true" }, true);
}
function isDataTable(id) {
testAbsentAttrs(id, { "layout-guess": "true" });
}
function doTest() {
// table with role of grid
testAbsentAttrs("table1", attr);
isDataTable("table1");
// table with role of grid and datatable="0"
testAbsentAttrs("table1.1", attr);
isDataTable("table1.1");
// table with landmark role
testAbsentAttrs("table2", attr);
isDataTable("table2");
// table with summary
testAbsentAttrs("table3", attr);
isDataTable("table3");
// table with caption
testAbsentAttrs("table4", attr);
isDataTable("table4");
// layout table with empty caption
testAttrs("table4.2", attr, true);
isLayoutTable("table4.2");
// table with thead element
testAbsentAttrs("table5", attr);
isDataTable("table5");
// table with tfoot element
testAbsentAttrs("table5.1", attr);
isDataTable("table5.1");
// table with colgroup or col elements
testAbsentAttrs("table5.2", attr);
testAbsentAttrs("table5.3", attr);
isDataTable("table5.2");
isDataTable("table5.3");
// table with th element
testAbsentAttrs("table6", attr);
isDataTable("table6");
// table with headers attribute
testAbsentAttrs("table6.2", attr);
isDataTable("table6.2");
// table with scope attribute
testAbsentAttrs("table6.2.2", attr);
isDataTable("table6.2.2");
// table with abbr attribute
testAbsentAttrs("table6.2.3", attr);
isDataTable("table6.2.3");
// table with abbr element
testAbsentAttrs("table6.3", attr);
isDataTable("table6.3");
// table with abbr element having empty text node
testAbsentAttrs("table6.4", attr);
isDataTable("table6.4");
// table with abbr element and non-empty text node
testAttrs("table6.5", attr, true);
isLayoutTable("table6.5");
// layout table with nested table
testAttrs("table9", attr, true);
isLayoutTable("table9");
// layout table with 1 column
testAttrs("table10", attr, true);
isLayoutTable("table10");
// layout table with 1 row
testAttrs("table11", attr, true);
isLayoutTable("table11");
// table with 5 columns
testAbsentAttrs("table12", attr);
isDataTable("table12");
// table with a bordered cell
testAbsentAttrs("table13", attr);
isDataTable("table13");
// table with alternating row background colors
testAbsentAttrs("table14", attr);
isDataTable("table14");
// table with 3 columns and 21 rows
testAbsentAttrs("table15", attr);
isDataTable("table15");
// layout table that has a 100% width
testAttrs("table16", attr, true);
isLayoutTable("table16");
// layout table that has a 95% width in pixels
testAttrs("table17", attr, true);
isLayoutTable("table17");
// layout table with less than 10 columns
testAttrs("table18", attr, true);
isLayoutTable("table18");
// layout table with embedded iframe
testAttrs("table19", attr, true);
isLayoutTable("table19");
// tree grid, no layout table
testAbsentAttrs("table20", attr);
isDataTable("table20");
// layout table containing nested data table (having data structures)
testAttrs("table21", attr, true);
testAttrs("table21.2", attr, true);
testAttrs("table21.3", attr, true);
testAttrs("table21.4", attr, true);
testAttrs("table21.5", attr, true);
testAttrs("table21.6", attr, true);
isLayoutTable("table21");
isLayoutTable("table21.2");
isLayoutTable("table21.3");
isLayoutTable("table21.4");
isLayoutTable("table21.5");
isLayoutTable("table21.6");
// layout table having datatable="0" attribute and containing data table structure (tfoot element)
testAttrs("table22", attr, true);
isLayoutTable("table22");
// layout display:block table with 1 column
isLayoutTable("displayblock_table1");
// matrix
isDataTable("mtable1");
SimpleTest.finish();
}
@ -501,5 +509,32 @@
</tfoot>
</table>
<!-- display:block table -->
<table id="displayblock_table1" style="display:block">
<tr><td>Row1</td></tr>
<tr><td>Row2</td></tr>
</table>
<!-- MathML matrix -->
<math>
<mtable id="mtable1">
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
</mtable>
</math>
</body>
</html>