Bug 275010. Support AtkTable and start work to provide hint on layout vs. data table. r=surkov

This commit is contained in:
aaronleventhal%moonset.net 2006-07-24 14:26:25 +00:00
Родитель daf5a3d6c1
Коммит 0e74500a29
10 изменённых файлов: 274 добавлений и 75 удалений

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

@ -79,4 +79,9 @@ interface nsIAccessibleTable : nsISupports
boolean isColumnSelected (in long column);
boolean isRowSelected (in long row);
boolean isCellSelected (in long row, in long column);
/**
* Use heuristics to determine if table is most likely used for layout
*/
boolean isProbablyForLayout();
};

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

@ -361,16 +361,11 @@ nsAccessibleWrap::CreateMaiInterfaces(void)
}
//nsIAccessibleTable
if (accRole == nsIAccessible::ROLE_TREE_TABLE) {
// In most cases, html table is used as container to arrange the webpage,
// not to represent a "real" table with practical colum, colum heaer, row.
// So, only add maiInterfaceTable for XUL table.
nsCOMPtr<nsIAccessibleTable> accessInterfaceTable;
QueryInterface(NS_GET_IID(nsIAccessibleTable),
getter_AddRefs(accessInterfaceTable));
if (accessInterfaceTable) {
interfacesBits |= 1 << MAI_INTERFACE_TABLE;
}
nsCOMPtr<nsIAccessibleTable> accessInterfaceTable;
QueryInterface(NS_GET_IID(nsIAccessibleTable),
getter_AddRefs(accessInterfaceTable));
if (accessInterfaceTable) {
interfacesBits |= 1 << MAI_INTERFACE_TABLE;
}
return interfacesBits;

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

@ -52,6 +52,7 @@
#include "nsIDOMNSHTMLElement.h"
#include "nsIDOMViewCSS.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIFrame.h"
#include "nsIPrefService.h"
@ -446,21 +447,41 @@ NS_IMETHODIMP
nsAccessNode::GetComputedStyleValue(const nsAString& aPseudoElt, const nsAString& aPropertyName, nsAString& aValue)
{
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mDOMNode));
nsPresContext *presContext = GetPresContext();
NS_ENSURE_TRUE(domElement && presContext, NS_ERROR_FAILURE);
nsCOMPtr<nsISupports> container = presContext->GetContainer();
nsCOMPtr<nsIDOMWindow> domWin(do_GetInterface(container));
nsCOMPtr<nsIDOMViewCSS> viewCSS(do_QueryInterface(domWin));
NS_ENSURE_TRUE(viewCSS, NS_ERROR_FAILURE);
if (!domElement) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl;
viewCSS->GetComputedStyle(domElement, aPseudoElt, getter_AddRefs(styleDecl));
GetComputedStyleDeclaration(aPseudoElt, domElement, getter_AddRefs(styleDecl));
NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
return styleDecl->GetPropertyValue(aPropertyName, aValue);
}
void nsAccessNode::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
nsIDOMElement *aElement,
nsIDOMCSSStyleDeclaration **aCssDecl)
{
*aCssDecl = nsnull;
// Returns number of items in style declaration
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
if (!content) {
return;
}
nsCOMPtr<nsIDocument> doc = content->GetDocument();
if (!doc) {
return;
}
nsCOMPtr<nsIDOMViewCSS> viewCSS(do_QueryInterface(doc->GetWindow()));
if (!viewCSS) {
return;
}
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
viewCSS->GetComputedStyle(aElement, aPseudoElt, getter_AddRefs(cssDecl));
NS_IF_ADDREF(*aCssDecl = cssDecl);
}
/***************** Hashtable of nsIAccessNode's *****************/
already_AddRefed<nsIAccessibleDocument>
@ -563,3 +584,5 @@ void nsAccessNode::ClearCache(nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode>
{
aCache.Enumerate(ClearCacheEntry, nsnull);
}

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

@ -139,6 +139,10 @@ class nsAccessNode: public nsIAccessNode, public nsPIAccessNode
aContent->GetAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role, aRole);
}
static void GetComputedStyleDeclaration(const nsAString& aPseudoElt,
nsIDOMElement *aElement,
nsIDOMCSSStyleDeclaration **aCssDecl);
already_AddRefed<nsRootAccessible> GetRootAccessible();
static nsIDOMNode *gLastFocusedNode;

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

@ -69,6 +69,7 @@ ACCESSIBILITY_ATOM(_true, "true")
ACCESSIBILITY_ATOM(headerContentLanguage, "content-language")
// Alphabetical list of frame types
ACCESSIBILITY_ATOM(areaFrame, "AreaFrame")
ACCESSIBILITY_ATOM(blockFrame, "BlockFrame")
ACCESSIBILITY_ATOM(brFrame, "BRFrame")
ACCESSIBILITY_ATOM(inlineBlockFrame, "InlineBlockFrame")
@ -76,7 +77,7 @@ ACCESSIBILITY_ATOM(inlineFrame, "InlineFrame")
ACCESSIBILITY_ATOM(objectFrame, "ObjectFrame")
ACCESSIBILITY_ATOM(textFrame, "TextFrame")
ACCESSIBILITY_ATOM(tableCellFrame, "TableCellFrame")
ACCESSIBILITY_ATOM(areaFrame, "AreaFrame")
ACCESSIBILITY_ATOM(tableOuterFrame, "TableOuterFrame")
// Alphabetical list of tag names
ACCESSIBILITY_ATOM(a, "a")

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

@ -1694,7 +1694,7 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
// Please leave this in for now, it's a convenient debugging method
nsAutoString name;
aNode->GetLocalName(name);
if (name.LowerCaseEqualsLiteral("wizardpage"))
if (name.LowerCaseEqualsLiteral("table"))
printf("## aaronl debugging tag name\n");
nsAutoString attrib;
@ -1868,6 +1868,11 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
}
}
else { // HTML accessibles
// Prefer to use markup (mostly tag name, perhaps attributes) to
// decide if and what kind of accessible to create.
CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode, role, getter_AddRefs(newAcc));
PRBool tryFrame = (newAcc == nsnull);
if (!content->IsFocusable()) {
// If we're in unfocusable table-related subcontent, check for the
// Presentation role on the containing table
@ -1882,20 +1887,22 @@ NS_IMETHODIMP nsAccessibilityService::GetAccessible(nsIDOMNode *aNode,
nsAutoString tableRole;
while ((tableContent = tableContent->GetParent()) != nsnull) {
if (tableContent->Tag() == nsAccessibilityAtoms::table) {
if (nsAccessNode::HasRoleAttribute(tableContent)) {
// Table that we're a descendant of is presentational
return NS_ERROR_FAILURE;
nsIFrame *tableFrame = aPresShell->GetPrimaryFrameFor(tableContent);
if (!tableFrame || tableFrame->GetType() != nsAccessibilityAtoms::tableOuterFrame ||
nsAccessNode::HasRoleAttribute(tableContent)) {
// Table that we're a descendant of is not styled as a table,
// and has no table accessible for an ancestor, or
// table that we're a descendant of is presentational
tryFrame = PR_FALSE;
}
break;
}
}
}
}
// Prefer to use markup (mostly tag name, perhaps attributes) to
// decide if and what kind of accessible to create.
CreateHTMLAccessibleByMarkup(frame, aWeakShell, aNode, role, getter_AddRefs(newAcc));
if (!newAcc) {
if (tryFrame) {
frame->GetAccessible(getter_AddRefs(newAcc)); // Try using frame to do it
}
}

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

@ -41,6 +41,7 @@
#include "nsIDOMElement.h"
#include "nsINameSpaceManager.h"
#include "nsIAccessibilityService.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMHTMLTableCaptionElem.h"
#include "nsIDOMHTMLTableCellElement.h"
@ -48,6 +49,7 @@
#include "nsIDOMHTMLTableRowElement.h"
#include "nsIDOMHTMLTableSectionElem.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIPresShell.h"
#include "nsIServiceManager.h"
#include "nsITableLayout.h"
@ -601,6 +603,190 @@ nsHTMLTableAccessible::GetCellAt(PRInt32 aRowIndex,
isSelected);
}
#ifdef SHOW_LAYOUT_HEURISTIC
NS_IMETHODIMP nsHTMLTableAccessible::GetDescription(nsAString& aDescription)
{
// Helpful for debugging layout vs. data tables
aDescription.Truncate();
PRBool isProbablyForLayout;
IsProbablyForLayout(&isProbablyForLayout);
aDescription = mLayoutHeuristic;
#ifdef DEBUG
printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get());
#endif
return NS_OK;
}
#endif
PRBool nsHTMLTableAccessible::HasDescendant(char *aTagName)
{
nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mDOMNode));
NS_ENSURE_TRUE(tableElt, PR_FALSE);
nsCOMPtr<nsIDOMNodeList> nodeList;
nsAutoString tagName;
tagName.AssignWithConversion(aTagName);
tableElt->GetElementsByTagName(tagName, getter_AddRefs(nodeList));
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
PRUint32 length;
nodeList->GetLength(&length);
if (length == 1) {
// Make sure it's not the table itself
nsCOMPtr<nsIDOMNode> firstItem;
nodeList->Item(0, getter_AddRefs(firstItem));
return firstItem != mDOMNode;
}
return length > 0;
}
NS_IMETHODIMP nsHTMLTableAccessible::IsProbablyForLayout(PRBool *aIsProbablyForLayout)
{
// 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) \
{ *aIsProbablyForLayout = isLayout; \
mLayoutHeuristic = isLayout ? NS_LITERAL_STRING("layout table: ") : NS_LITERAL_STRING("data table: "); \
mLayoutHeuristic += NS_LITERAL_STRING(heuristic); return NS_OK; }
#else
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
#endif
*aIsProbablyForLayout = PR_FALSE;
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
if (!content) {
return NS_ERROR_FAILURE; // Table shut down
}
// Check role and role attribute
PRBool hasNonTableRole = (Role(this) != ROLE_TABLE);
if (hasNonTableRole) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute");
}
if (HasRoleAttribute(content)) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has role attribute, and role is table");
}
// Check for legitimate data table elements or attributes
if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary) || HasDescendant("summary") ||
HasDescendant("th") || HasDescendant("thead") || HasDescendant("tfoot") || HasDescendant("colgroup")) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures");
}
if (HasDescendant("table")) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has a nested table within it");
}
// If only 1 column or only 1 row, it's for layout
PRInt32 columns, rows;
GetColumns(&columns);
if (columns <=1) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column");
}
GetRows(&rows);
if (rows <=1) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 row");
}
// Check for many columns
if (columns >= 5) {
RETURN_LAYOUT_ANSWER(PR_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?
nsCOMPtr<nsIDOMElement> cellElement;
GetCellAt(0, 0, *getter_AddRefs(cellElement));
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE);
nsCOMPtr<nsIPresShell> shell(GetPresShell());
nsIFrame *cellFrame = shell->GetPrimaryFrameFor(cellContent);
NS_ENSURE_TRUE(cellFrame, NS_ERROR_FAILURE);
nsMargin border;
cellFrame->GetBorder(border);
if (border.top && border.bottom && border.left && border.right) {
RETURN_LAYOUT_ANSWER(PR_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 the row
// Alternating background color is a common way
nsCOMPtr<nsIDOMNodeList> nodeList;
nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mDOMNode));
tableElt->GetElementsByTagName(NS_LITERAL_STRING("tr"), getter_AddRefs(nodeList));
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
PRUint32 length;
nodeList->GetLength(&length);
nsAutoString color, lastRowColor;
for (PRInt32 rowCount = 0; rowCount < rows; rowCount ++) {
nsCOMPtr<nsIDOMNode> rowNode;
nodeList->Item(rowCount, getter_AddRefs(rowNode));
nsCOMPtr<nsIDOMElement> rowElement = do_QueryInterface(rowNode);
nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl;
GetComputedStyleDeclaration(EmptyString(), rowElement, getter_AddRefs(styleDecl));
NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
lastRowColor = color;
styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color);
if (rowCount > 0 && PR_FALSE == lastRowColor.Equals(color)) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "2 styles of row background color, non-bordered");
}
}
// Check for many rows
const PRInt32 kMaxLayoutRows = 20;
if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data
RETURN_LAYOUT_ANSWER(PR_TRUE, ">= kMaxLayoutRows (20) and non-bordered");
}
// Check for very wide table
nsAutoString styledWidth;
GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth);
if (styledWidth.EqualsLiteral("100%")) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns and 100% width");
}
if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels
nsIFrame *tableFrame = GetFrame();
NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE);
nsSize tableSize = tableFrame->GetSize();
nsCOMPtr<nsIAccessibleDocument> docAccessible = GetDocAccessible();
nsCOMPtr<nsPIAccessNode> docAccessNode(do_QueryInterface(docAccessible));
NS_ENSURE_TRUE(docAccessNode, NS_ERROR_FAILURE);
nsIFrame *docFrame = docAccessNode->GetFrame();
NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE);
nsSize docSize = docFrame->GetSize();
PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.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(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
}
}
// Two column rules
if (rows * columns <= 10) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered");
}
if (HasDescendant("embed") || HasDescendant("object") || HasDescendant("applet") || HasDescendant("iframe")) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
}
RETURN_LAYOUT_ANSWER(PR_FALSE, "no layout factor strong enough, so will guess data");
}
// --------------------------------------------------------
// nsHTMLTableHeadAccessible Accessible
// --------------------------------------------------------
@ -662,3 +848,4 @@ nsHTMLTableHeadAccessible::GetRows(PRInt32 *aRows)
return rows->GetLength((PRUint32 *)aRows);
}

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

@ -54,6 +54,11 @@ public:
class nsITableLayout;
// 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
#define SHOW_LAYOUT_HEURISTIC
class nsHTMLTableAccessible : public nsAccessibleWrap
{
public:
@ -64,6 +69,9 @@ public:
NS_IMETHOD GetRole(PRUint32 *aResult);
NS_IMETHOD GetState(PRUint32 *aResult);
NS_IMETHOD GetName(nsAString& aResult);
#ifdef SHOW_LAYOUT_HEURISTIC
NS_IMETHOD GetDescription(nsAString& aDescription);
#endif
protected:
nsresult GetTableNode(nsIDOMNode **_retval);
@ -71,9 +79,12 @@ protected:
nsresult GetCellAt(PRInt32 aRowIndex,
PRInt32 aColIndex,
nsIDOMElement* &aCell);
PRBool HasDescendant(char *aTagName);
#ifdef SHOW_LAYOUT_HEURISTIC
nsAutoString mLayoutHeuristic;
#endif
};
class nsHTMLTableHeadAccessible : public nsHTMLTableAccessible
{
public:

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

@ -250,41 +250,6 @@ STDMETHODIMP nsAccessNodeWrap::get_attributesForNames(
return S_OK;
}
NS_IMETHODIMP nsAccessNodeWrap::GetComputedStyleDeclaration(nsIDOMCSSStyleDeclaration **aCssDecl, PRUint32 *aLength)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
if (!content)
return NS_ERROR_FAILURE;
if (content->IsNodeOfType(nsINode::eTEXT)) {
content = content->GetParent();
NS_ASSERTION(content, "No parent for text node");
}
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(content));
nsCOMPtr<nsIDocument> doc = content->GetDocument();
if (!domElement || !doc) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMViewCSS> viewCSS(do_QueryInterface(doc->GetWindow()));
if (!viewCSS)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
viewCSS->GetComputedStyle(domElement, EmptyString(), getter_AddRefs(cssDecl));
if (cssDecl) {
*aCssDecl = cssDecl;
NS_ADDREF(*aCssDecl);
cssDecl->GetLength(aLength);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
/* To do: use media type if not null */
STDMETHODIMP nsAccessNodeWrap::get_computedStyle(
/* [in] */ unsigned short aMaxStyleProperties,
@ -293,14 +258,17 @@ STDMETHODIMP nsAccessNodeWrap::get_computedStyle(
/* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues,
/* [out] */ unsigned short __RPC_FAR *aNumStyleProperties)
{
if (!mDOMNode)
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mDOMNode));
if (!domElement)
return E_FAIL;
*aNumStyleProperties = 0;
PRUint32 length;
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
if (NS_FAILED(GetComputedStyleDeclaration(getter_AddRefs(cssDecl), &length)))
return E_FAIL;
GetComputedStyleDeclaration(EmptyString(), domElement, getter_AddRefs(cssDecl));
NS_ENSURE_TRUE(cssDecl, E_FAIL);
PRUint32 length;
cssDecl->GetLength(&length);
PRUint32 index, realIndex;
for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; index ++) {
@ -325,14 +293,13 @@ STDMETHODIMP nsAccessNodeWrap::get_computedStyleForProperties(
/* [length_is][size_is][in] */ BSTR __RPC_FAR *aStyleProperties,
/* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues)
{
if (!mDOMNode)
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mDOMNode));
if (!domElement)
return E_FAIL;
PRUint32 length = 0;
nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
nsresult rv = GetComputedStyleDeclaration(getter_AddRefs(cssDecl), &length);
if (NS_FAILED(rv))
return E_FAIL;
GetComputedStyleDeclaration(EmptyString(), domElement, getter_AddRefs(cssDecl));
NS_ENSURE_TRUE(cssDecl, E_FAIL);
PRUint32 index;
for (index = 0; index < aNumStyleProperties; index ++) {

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

@ -136,7 +136,6 @@ class nsAccessNodeWrap : public nsAccessNode, public ISimpleDOMNode
protected:
void GetAccessibleFor(nsIDOMNode *node, nsIAccessible **newAcc);
ISimpleDOMNode* MakeAccessNode(nsIDOMNode *node);
NS_IMETHOD GetComputedStyleDeclaration(nsIDOMCSSStyleDeclaration **aCssDecl, PRUint32 *aLength);
static PRBool gIsEnumVariantSupportDisabled;
};