void XULListCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) { TableAccessible* table = Table(); NS_ASSERTION(table, "cell not in a table!"); if (!table) return; // Get column header cell from XUL listhead. Accessible* list = nullptr; Accessible* tableAcc = table->AsAccessible(); uint32_t tableChildCount = tableAcc->ChildCount(); for (uint32_t childIdx = 0; childIdx < tableChildCount; childIdx++) { Accessible* child = tableAcc->GetChildAt(childIdx); if (child->Role() == roles::LIST) { list = child; break; } } if (list) { Accessible* headerCell = list->GetChildAt(ColIdx()); if (headerCell) { aCells->AppendElement(headerCell); return; } } // No column header cell from XUL markup, try to get it from ARIA markup. TableCellAccessible::ColHeaderCells(aCells); }
nsresult ARIAGridCellAccessible::GetAttributesInternal(nsIPersistentProperties* aAttributes) { if (IsDefunct()) return NS_ERROR_FAILURE; nsresult rv = HyperTextAccessibleWrap::GetAttributesInternal(aAttributes); NS_ENSURE_SUCCESS(rv, rv); // Expose "table-cell-index" attribute. Accessible* thisRow = Parent(); if (!thisRow || thisRow->Role() != roles::ROW) return NS_OK; int32_t colIdx = 0, colCount = 0; uint32_t childCount = thisRow->ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = thisRow->GetChildAt(childIdx); if (child == this) colIdx = colCount; roles::Role role = child->Role(); if (role == roles::GRID_CELL || role == roles::ROWHEADER || role == roles::COLUMNHEADER) colCount++; } Accessible* table = thisRow->Parent(); if (!table) return NS_OK; roles::Role tableRole = table->Role(); if (tableRole != roles::TABLE && tableRole != roles::TREE_TABLE) return NS_OK; int32_t rowIdx = 0; childCount = table->ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = table->GetChildAt(childIdx); if (child == thisRow) break; if (child->Role() == roles::ROW) rowIdx++; } int32_t idx = rowIdx * colCount + colIdx; nsAutoString stringIdx; stringIdx.AppendInt(idx); nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::tableCellIndex, stringIdx); return NS_OK; }
void TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const { if (mStartContainer == mEndContainer) { int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset); int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset); for (int32_t idx = startIdx; idx <= endIdx; idx++) { Accessible* child = mStartContainer->GetChildAt(idx); if (nsAccUtils::IsEmbeddedObject(child)) aChildren->AppendElement(child); } return; } Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset); Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset); uint32_t pos1 = 0, pos2 = 0; AutoTArray<Accessible*, 30> parents1, parents2; Accessible* container = CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2); // Traverse the tree up to the container and collect embedded objects. for (uint32_t idx = 0; idx < pos1 - 1; idx++) { Accessible* parent = parents1[idx + 1]; Accessible* child = parents1[idx]; uint32_t childCount = parent->ChildCount(); for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount; childIdx++) { Accessible* next = parent->GetChildAt(childIdx); if (nsAccUtils::IsEmbeddedObject(next)) aChildren->AppendElement(next); } } // Traverse through direct children in the container. int32_t endIdx = parents2[pos2 - 1]->IndexInParent(); int32_t childIdx = parents1[pos1 - 1]->IndexInParent() + 1; for (; childIdx < endIdx; childIdx++) { Accessible* next = container->GetChildAt(childIdx); if (nsAccUtils::IsEmbeddedObject(next)) aChildren->AppendElement(next); } // Traverse down from the container to end point. for (int32_t idx = pos2 - 2; idx > 0; idx--) { Accessible* parent = parents2[idx]; Accessible* child = parents2[idx - 1]; int32_t endIdx = child->IndexInParent(); for (int32_t childIdx = 0; childIdx < endIdx; childIdx++) { Accessible* next = parent->GetChildAt(childIdx); if (nsAccUtils::IsEmbeddedObject(next)) aChildren->AppendElement(next); } } }
void XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet, int32_t* aSetSize) { int32_t setSize = 0; int32_t posInSet = 0; Accessible* parent = Parent(); if (!parent) return; uint32_t childCount = parent->ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = parent->GetChildAt(childIdx); if (IsSeparator(child)) { // end of a group of buttons if (posInSet) break; // we've found our group, so we're done setSize = 0; // not our group, so start a new group } else { setSize++; // another button in the group if (child == this) posInSet = setSize; // we've found our button } } *aPosInSet = posInSet; *aSetSize = setSize; }
nsIContent* nsXFormsSelectableAccessible::GetItemByIndex(PRUint32* aIndex, Accessible* aAccessible) { Accessible* accessible = aAccessible ? aAccessible : this; PRUint32 childCount = accessible->ChildCount(); for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = accessible->GetChildAt(childIdx); nsIContent* childContent = child->GetContent(); nsINodeInfo *nodeInfo = childContent->NodeInfo(); if (nodeInfo->NamespaceEquals(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS))) { if (nodeInfo->Equals(nsGkAtoms::item)) { if (!*aIndex) return childContent; --*aIndex; } else if (nodeInfo->Equals(nsGkAtoms::choices)) { nsIContent* itemContent = GetItemByIndex(aIndex, child); if (itemContent) return itemContent; } } } return nsnull; }
NS_IMETHODIMP ARIAGridCellAccessible::GetRowIndex(int32_t* aRowIndex) { NS_ENSURE_ARG_POINTER(aRowIndex); *aRowIndex = -1; if (IsDefunct()) return NS_ERROR_FAILURE; Accessible* row = Parent(); if (!row) return NS_OK; Accessible* table = row->Parent(); if (!table) return NS_OK; *aRowIndex = 0; int32_t indexInTable = row->IndexInParent(); for (int32_t idx = 0; idx < indexInTable; idx++) { row = table->GetChildAt(idx); if (row->Role() == roles::ROW) (*aRowIndex)++; } return NS_OK; }
Accessible* AccGroupInfo::NextItemTo(Accessible* aItem) { AccGroupInfo* groupInfo = aItem->GetGroupInfo(); if (!groupInfo) return nullptr; // If the item in middle of the group then search next item in siblings. if (groupInfo->PosInSet() >= groupInfo->SetSize()) return nullptr; Accessible* parent = aItem->Parent(); uint32_t childCount = parent->ChildCount(); for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) { Accessible* nextItem = parent->GetChildAt(idx); AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo(); if (nextGroupInfo && nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) { return nextItem; } } NS_NOTREACHED("Item in the middle of the group but there's no next item!"); return nullptr; }
NS_IMETHODIMP ARIAGridCellAccessible::GetColumnIndex(int32_t* aColumnIndex) { NS_ENSURE_ARG_POINTER(aColumnIndex); *aColumnIndex = -1; if (IsDefunct()) return NS_ERROR_FAILURE; Accessible* row = Parent(); if (!row) return NS_OK; *aColumnIndex = 0; int32_t indexInRow = IndexInParent(); for (int32_t idx = 0; idx < indexInRow; idx++) { Accessible* cell = row->GetChildAt(idx); roles::Role role = cell->Role(); if (role == roles::GRID_CELL || role == roles::ROWHEADER || role == roles::COLUMNHEADER) (*aColumnIndex)++; } return NS_OK; }
Accessible* nsAccessiblePivot::SearchBackward(Accessible* aAccessible, nsIAccessibleTraversalRule* aRule, bool aSearchCurrent, nsresult* aResult) { *aResult = NS_OK; // Initial position could be unset, in that case return null. if (!aAccessible) return nullptr; RuleCache cache(aRule); uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; Accessible* accessible = AdjustStartPosition(aAccessible, cache, &filtered, aResult); NS_ENSURE_SUCCESS(*aResult, nullptr); if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) { return accessible; } Accessible* root = GetActiveRoot(); while (accessible != root) { Accessible* parent = accessible->Parent(); int32_t idxInParent = accessible->IndexInParent(); while (idxInParent > 0) { if (!(accessible = parent->GetChildAt(--idxInParent))) continue; *aResult = cache.ApplyFilter(accessible, &filtered); NS_ENSURE_SUCCESS(*aResult, nullptr); Accessible* lastChild = nullptr; while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) && (lastChild = accessible->LastChild())) { parent = accessible; accessible = lastChild; idxInParent = accessible->IndexInParent(); *aResult = cache.ApplyFilter(accessible, &filtered); NS_ENSURE_SUCCESS(*aResult, nullptr); } if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) return accessible; } if (!(accessible = parent)) break; *aResult = cache.ApplyFilter(accessible, &filtered); NS_ENSURE_SUCCESS(*aResult, nullptr); if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) return accessible; } return nullptr; }
void XULTreeGridAccessible::ColDescription(uint32_t aColIdx, nsString& aDescription) { aDescription.Truncate(); Accessible* treeColumns = Accessible::GetChildAt(0); if (treeColumns) { Accessible* treeColumnItem = treeColumns->GetChildAt(aColIdx); if (treeColumnItem) treeColumnItem->Name(aDescription); } }
uint32_t ARIAGridCellAccessible::ColIdx() const { Accessible* row = Row(); if (!row) return 0; int32_t indexInRow = IndexInParent(); uint32_t colIdx = 0; for (int32_t idx = 0; idx < indexInRow; idx++) { Accessible* cell = row->GetChildAt(idx); if (cell->IsTableCell()) { colIdx += cell->AsTableCell()->ColExtent(); } } return colIdx; }
NS_IMETHODIMP nsXULListCellAccessible::GetColumnHeaderCells(nsIArray **aHeaderCells) { NS_ENSURE_ARG_POINTER(aHeaderCells); *aHeaderCells = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr<nsIAccessibleTable> table; GetTable(getter_AddRefs(table)); NS_ENSURE_STATE(table); // we expect to be in a listbox (table) // Get column header cell from XUL listhead. Accessible* list = nsnull; nsRefPtr<Accessible> tableAcc(do_QueryObject(table)); PRUint32 tableChildCount = tableAcc->ChildCount(); for (PRUint32 childIdx = 0; childIdx < tableChildCount; childIdx++) { Accessible* child = tableAcc->GetChildAt(childIdx); if (child->Role() == roles::LIST) { list = child; break; } } if (list) { PRInt32 colIdx = -1; GetColumnIndex(&colIdx); nsIAccessible *headerCell = list->GetChildAt(colIdx); if (headerCell) { nsresult rv = NS_OK; nsCOMPtr<nsIMutableArray> headerCells = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); headerCells->AppendElement(headerCell, false); NS_ADDREF(*aHeaderCells = headerCells); return NS_OK; } } // No column header cell from XUL markup, try to get it from ARIA markup. return nsAccUtils::GetHeaderCellsFor(table, this, nsAccUtils::eColumnHeaderCells, aHeaderCells); }
uint32_t XULListCellAccessible::ColIdx() const { Accessible* row = Parent(); if (!row) return 0; int32_t indexInRow = IndexInParent(); uint32_t colIdx = 0; for (int32_t idx = 0; idx < indexInRow; idx++) { Accessible* cell = row->GetChildAt(idx); roles::Role role = cell->Role(); if (role == roles::CELL || role == roles::GRID_CELL || role == roles::ROWHEADER || role == roles::COLUMNHEADER) colIdx++; } return colIdx; }
uint32_t XULListCellAccessible::RowIdx() const { Accessible* row = Parent(); if (!row) return 0; Accessible* table = row->Parent(); if (!table) return 0; int32_t indexInTable = row->IndexInParent(); uint32_t rowIdx = 0; for (int32_t idx = 0; idx < indexInTable; idx++) { row = table->GetChildAt(idx); if (row->Role() == roles::ROW) rowIdx++; } return rowIdx; }
Accessible* XULListboxAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) { nsCOMPtr<nsIDOMXULSelectControlElement> control = do_QueryInterface(mContent); NS_ENSURE_TRUE(control, nullptr); nsCOMPtr<nsIDOMXULSelectControlItemElement> item; control->GetItemAtIndex(aRowIndex, getter_AddRefs(item)); if (!item) return nullptr; nsCOMPtr<nsIContent> itemContent(do_QueryInterface(item)); if (!itemContent) return nullptr; Accessible* row = mDoc->GetAccessible(itemContent); NS_ENSURE_TRUE(row, nullptr); return row->GetChildAt(aColumnIndex); }
already_AddRefed<nsIPersistentProperties> ARIAGridCellAccessible::NativeAttributes() { nsCOMPtr<nsIPersistentProperties> attributes = HyperTextAccessibleWrap::NativeAttributes(); // Expose "table-cell-index" attribute. Accessible* thisRow = Row(); if (!thisRow) return attributes.forget(); int32_t colIdx = 0, colCount = 0; uint32_t childCount = thisRow->ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = thisRow->GetChildAt(childIdx); if (child == this) colIdx = colCount; roles::Role role = child->Role(); if (role == roles::CELL || role == roles::GRID_CELL || role == roles::ROWHEADER || role == roles::COLUMNHEADER) colCount++; } int32_t rowIdx = RowIndexFor(thisRow); nsAutoString stringIdx; stringIdx.AppendInt(rowIdx * colCount + colIdx); nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); #ifdef DEBUG nsAutoString unused; attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"), NS_LITERAL_STRING("ARIAGridCellAccessible"), unused); #endif return attributes.forget(); }
void AccGroupInfo::Update() { Accessible* parent = mItem->Parent(); if (!parent) return; int32_t indexInParent = mItem->IndexInParent(); uint32_t siblingCount = parent->ChildCount(); if (indexInParent == -1 || indexInParent >= static_cast<int32_t>(siblingCount)) { NS_ERROR("Wrong index in parent! Tree invalidation problem."); return; } int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem); // Compute position in set. mPosInSet = 1; for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) { Accessible* sibling = parent->GetChildAt(idx); roles::Role siblingRole = sibling->Role(); // If the sibling is separator then the group is ended. if (siblingRole == roles::SEPARATOR) break; // If sibling is not visible and hasn't the same base role. if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE) continue; // Check if it's hierarchical flatten structure, i.e. if the sibling // level is lesser than this one then group is ended, if the sibling level // is greater than this one then the group is split by some child elements // (group will be continued). int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling); if (siblingLevel < level) { mParent = sibling; break; } // Skip subset. if (siblingLevel > level) continue; // If the previous item in the group has calculated group information then // build group information for this item based on found one. if (sibling->mBits.groupInfo) { mPosInSet += sibling->mBits.groupInfo->mPosInSet; mParent = sibling->mBits.groupInfo->mParent; mSetSize = sibling->mBits.groupInfo->mSetSize; return; } mPosInSet++; } // Compute set size. mSetSize = mPosInSet; for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) { Accessible* sibling = parent->GetChildAt(idx); roles::Role siblingRole = sibling->Role(); // If the sibling is separator then the group is ended. if (siblingRole == roles::SEPARATOR) break; // If sibling is visible and has the same base role if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE) continue; // and check if it's hierarchical flatten structure. int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling); if (siblingLevel < level) break; // Skip subset. if (siblingLevel > level) continue; // If the next item in the group has calculated group information then // build group information for this item based on found one. if (sibling->mBits.groupInfo) { mParent = sibling->mBits.groupInfo->mParent; mSetSize = sibling->mBits.groupInfo->mSetSize; return; } mSetSize++; } if (mParent) return; roles::Role parentRole = parent->Role(); if (ShouldReportRelations(mRole, parentRole)) mParent = parent; // ARIA tree and list can be arranged by using ARIA groups to organize levels. if (parentRole != roles::GROUPING) return; // Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a // parent. In other words the parent of the tree item will be a group and // the previous tree item of the group is a conceptual parent of the tree // item. if (mRole == roles::OUTLINEITEM) { Accessible* parentPrevSibling = parent->PrevSibling(); if (parentPrevSibling && parentPrevSibling->Role() == mRole) { mParent = parentPrevSibling; return; } } // Way #2 for ARIA list and tree: group is a child of an item. In other words // the parent of the item will be a group and containing item of the group is // a conceptual parent of the item. if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) { Accessible* grandParent = parent->Parent(); if (grandParent && grandParent->Role() == mRole) mParent = grandParent; } }
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" ); }
void RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode) { // Get popup accessible. There are cases when popup element isn't accessible // but an underlying widget is and behaves like popup, an example is // autocomplete popups. DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode); if (!document) return; Accessible* popup = document->GetAccessible(aPopupNode); if (!popup) { Accessible* popupContainer = document->GetContainerAccessible(aPopupNode); if (!popupContainer) return; uint32_t childCount = popupContainer->ChildCount(); for (uint32_t idx = 0; idx < childCount; idx++) { Accessible* child = popupContainer->GetChildAt(idx); if (child->IsAutoCompletePopup()) { popup = child; break; } } // No popup no events. Focus is managed by DOM. This is a case for // menupopups of menus on Linux since there are no accessible for popups. if (!popup) return; } // In case of autocompletes and comboboxes fire state change event for // expanded state. Note, HTML form autocomplete isn't a subject of state // change event because they aren't autocompletes strictly speaking. // When popup closes (except nested popups and menus) then fire focus event to // where it was. The focus event is expected even if popup didn't take a focus. static const uint32_t kNotifyOfFocus = 1; static const uint32_t kNotifyOfState = 2; uint32_t notifyOf = 0; // HTML select is target of popuphidding event. Otherwise get container // widget. No container widget means this is either tooltip or menupopup. // No events in the former case. Accessible* widget = nullptr; if (popup->IsCombobox()) { widget = popup; } else { widget = popup->ContainerWidget(); if (!widget) { if (!popup->IsMenuPopup()) return; widget = popup; } } if (popup->IsAutoCompletePopup()) { // No focus event for autocomplete because it's managed by // DOMMenuItemInactive events. if (widget->IsAutoComplete()) notifyOf = kNotifyOfState; } else if (widget->IsCombobox()) { // Fire focus for active combobox, otherwise the focus is managed by DOM // focus notifications. Always fire state change event. if (widget->IsActiveWidget()) notifyOf = kNotifyOfFocus; notifyOf |= kNotifyOfState; } else if (widget->IsMenuButton()) { // Can be a part of autocomplete. Accessible* compositeWidget = widget->ContainerWidget(); if (compositeWidget && compositeWidget->IsAutoComplete()) { widget = compositeWidget; notifyOf = kNotifyOfState; } // Autocomplete (like searchbar) can be inactive when popup hiddens notifyOf |= kNotifyOfFocus; } else if (widget == popup) { // Top level context menus and alerts. // Ignore submenus and menubar. When submenu is closed then sumbenu // container menuitem takes a focus via DOMMenuItemActive notification. // For menubars processing we listen DOMMenubarActive/Inactive // notifications. notifyOf = kNotifyOfFocus; } // Restore focus to where it was. if (notifyOf & kNotifyOfFocus) { FocusMgr()->ActiveItemChanged(nullptr); #ifdef A11Y_LOG if (logging::IsEnabled(logging::eFocus)) logging::ActiveItemChangeCausedBy("popuphiding", popup); #endif } // Fire expanded state change event. if (notifyOf & kNotifyOfState) { nsRefPtr<AccEvent> event = new AccStateChangeEvent(widget, states::EXPANDED, false); document->FireDelayedEvent(event); } }