nsAccessible* nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow) { if (aRow < 0 || IsDefunct()) return nsnull; PRInt32 rowCount = 0; nsresult rv = mTreeView->GetRowCount(&rowCount); if (NS_FAILED(rv) || aRow >= rowCount) return nsnull; void *key = reinterpret_cast<void*>(aRow); nsAccessible* cachedTreeItem = mAccessibleCache.GetWeak(key); if (cachedTreeItem) return cachedTreeItem; nsRefPtr<nsAccessible> treeItem = CreateTreeItemAccessible(aRow); if (treeItem) { if (mAccessibleCache.Put(key, treeItem)) { if (GetDocAccessible()->BindToDocument(treeItem, nsnull)) return treeItem; mAccessibleCache.Remove(key); } } return nsnull; }
void nsHTMLImageMapAccessible::CacheChildren() { if (!mMapElement) return; nsCOMPtr<nsIDOMHTMLCollection> mapAreas; mMapElement->GetAreas(getter_AddRefs(mapAreas)); if (!mapAreas) return; nsDocAccessible* document = GetDocAccessible(); PRUint32 areaCount = 0; mapAreas->GetLength(&areaCount); for (PRUint32 areaIdx = 0; areaIdx < areaCount; areaIdx++) { nsCOMPtr<nsIDOMNode> areaNode; mapAreas->Item(areaIdx, getter_AddRefs(areaNode)); if (!areaNode) return; nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode)); nsRefPtr<nsAccessible> area = new nsHTMLAreaAccessible(areaContent, mWeakShell); if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) || !AppendChild(area)) { return; } } }
NS_IMETHODIMP nsAccessNode::GetDocument(nsIAccessibleDocument **aDocument) { NS_ENSURE_ARG_POINTER(aDocument); NS_IF_ADDREF(*aDocument = GetDocAccessible()); return NS_OK; }
void nsXULButtonAccessible::CacheChildren() { // In general XUL button has not accessible children. Nevertheless menu // buttons can have button (@type="menu-button") and popup accessibles // (@type="menu-button" or @type="menu"). // XXX: no children until the button is menu button. Probably it's not // totally correct but in general AT wants to have leaf buttons. PRBool isMenu = mContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type, nsAccessibilityAtoms::menu, eCaseMatters); PRBool isMenuButton = isMenu ? PR_FALSE : mContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type, nsAccessibilityAtoms::menuButton, eCaseMatters); if (!isMenu && !isMenuButton) return; nsAccessible* menupopup = nsnull; nsAccessible* button = nsnull; nsAccTreeWalker walker(mWeakShell, mContent, PR_TRUE); nsAccessible* child = nsnull; while ((child = walker.NextChild())) { PRUint32 role = child->Role(); if (role == nsIAccessibleRole::ROLE_MENUPOPUP) { // Get an accessible for menupopup or panel elements. menupopup = child; } else if (isMenuButton && role == nsIAccessibleRole::ROLE_PUSHBUTTON) { // Button type="menu-button" contains a real button. Get an accessible // for it. Ignore dropmarker button which is placed as a last child. button = child; break; } else { // Unbind rejected accessible from document. GetDocAccessible()->UnbindFromDocument(child); } } if (!menupopup) return; AppendChild(menupopup); if (button) AppendChild(button); }
nsHTMLLIAccessible:: nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell) : nsHyperTextAccessibleWrap(aContent, aShell), mBullet(nsnull) { mFlags |= eHTMLListItemAccessible; nsBlockFrame* blockFrame = do_QueryFrame(GetFrame()); if (blockFrame && blockFrame->HasBullet()) { mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell); if (!GetDocAccessible()->BindToDocument(mBullet, nsnull)) mBullet = nsnull; } }
void nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount) { if (IsDefunct()) return; // Do not invalidate the cache if rows have been inserted. if (aCount > 0) return; nsDocAccessible* document = GetDocAccessible(); // Fire destroy event for removed tree items and delete them from caches. for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { void* key = reinterpret_cast<void*>(rowIdx); nsAccessible* treeItem = mAccessibleCache.GetWeak(key); if (treeItem) { nsRefPtr<AccEvent> event = new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem); nsEventShell::FireEvent(event); // Unbind from document, shutdown and remove from tree cache. document->UnbindFromDocument(treeItem); mAccessibleCache.Remove(key); } } // We dealt with removed tree items already however we may keep tree items // having row indexes greater than row count. We should remove these dead tree // items silently from caches. PRInt32 newRowCount = 0; nsresult rv = mTreeView->GetRowCount(&newRowCount); if (NS_FAILED(rv)) return; PRInt32 oldRowCount = newRowCount - aCount; for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { void *key = reinterpret_cast<void*>(rowIdx); nsAccessible* treeItem = mAccessibleCache.GetWeak(key); if (treeItem) { // Unbind from document, shutdown and remove from tree cache. document->UnbindFromDocument(treeItem); mAccessibleCache.Remove(key); } } }
nsresult nsHTMLTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { nsresult rv = nsTextAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); nsDocAccessible *docAccessible = GetDocAccessible(); if (docAccessible) { PRUint32 state, extState; docAccessible->GetState(&state, &extState); if (0 == (extState & nsIAccessibleStates::EXT_STATE_EDITABLE)) { *aState |= nsIAccessibleStates::STATE_READONLY; // Links not focusable in editor } } return NS_OK; }
void nsXULTreeAccessible::TreeViewChanged() { if (IsDefunct()) return; // Fire reorder event on tree accessible on accessible tree (do not fire // show/hide events on tree items because it can be expensive to fire them for // each tree item. nsRefPtr<AccEvent> reorderEvent = new AccEvent(nsIAccessibleEvent::EVENT_REORDER, this, eAutoDetect, AccEvent::eCoalesceFromSameSubtree); if (reorderEvent) GetDocAccessible()->FireDelayedAccessibleEvent(reorderEvent); // Clear cache. ClearCache(mAccessibleCache); mTree->GetView(getter_AddRefs(mTreeView)); }
void nsHTMLLIAccessible::UpdateBullet(bool aHasBullet) { if (aHasBullet == !!mBullet) { NS_NOTREACHED("Bullet and accessible are in sync already!"); return; } nsDocAccessible* document = GetDocAccessible(); if (aHasBullet) { mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell); if (document->BindToDocument(mBullet, nsnull)) { InsertChildAt(0, mBullet); } } else { RemoveChild(mBullet); document->UnbindFromDocument(mBullet); mBullet = nsnull; } // XXXtodo: fire show/hide and reorder events. That's hard to make it // right now because coalescence happens by DOM node. }
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; if (IsDefunct()) return NS_ERROR_FAILURE; nsDocAccessible *docAccessible = GetDocAccessible(); if (docAccessible) { PRUint32 state, extState; docAccessible->GetState(&state, &extState); if (extState & nsIAccessibleStates::EXT_STATE_EDITABLE) { // Need to see all elements while document is being edited RETURN_LAYOUT_ANSWER(PR_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). PRBool hasNonTableRole = (nsAccUtils::Role(this) != nsIAccessibleRole::ROLE_TABLE); if (hasNonTableRole) { RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute"); } if (mContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::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(PR_FALSE, "Has role attribute, weak role, and role is table"); } // Check for legitimate data table elements or attributes nsAutoString summary; if ((mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, summary) && !summary.IsEmpty()) || HasDescendant(NS_LITERAL_STRING("caption"), PR_FALSE) || HasDescendant(NS_LITERAL_STRING("th")) || HasDescendant(NS_LITERAL_STRING("thead")) || HasDescendant(NS_LITERAL_STRING("tfoot")) || HasDescendant(NS_LITERAL_STRING("colgroup"))) { RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures"); } if (HasDescendant(NS_LITERAL_STRING("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; GetColumnCount(&columns); if (columns <=1) { RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column"); } GetRowCount(&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; nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement)); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement)); NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE); nsIFrame *cellFrame = cellContent->GetPrimaryFrame(); if (!cellFrame) { return NS_OK; } 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(mContent)); 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 (PRUint32 rowCount = 0; rowCount < length; rowCount ++) { nsCOMPtr<nsIDOMNode> rowNode; nodeList->Item(rowCount, getter_AddRefs(rowNode)); nsCOMPtr<nsIContent> rowContent(do_QueryInterface(rowNode)); nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl = nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowContent); 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_FALSE, ">= 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(); nsDocAccessible *docAccessible = GetDocAccessible(); NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE); nsIFrame *docFrame = docAccessible->GetFrame(); NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE); nsSize docSize = docFrame->GetSize(); if (docSize.width > 0) { 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(NS_LITERAL_STRING("embed")) || HasDescendant(NS_LITERAL_STRING("object")) || HasDescendant(NS_LITERAL_STRING("applet")) || HasDescendant(NS_LITERAL_STRING("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"); }