зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1460244
- Tables with CSS display properties no longer participate in layout table calculation, r=marcoz
This commit is contained in:
Родитель
2cf03f5d2f
Коммит
246756a336
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче