HyperTextAccessible* nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward) { Accessible* root = GetActiveRoot(); Accessible* accessible = aAccessible; while (true) { Accessible* child = nullptr; while ((child = (aBackward ? accessible->LastChild() : accessible->FirstChild()))) { accessible = child; if (child->IsHyperText()) return child->AsHyperText(); } Accessible* sibling = nullptr; Accessible* temp = accessible; do { if (temp == root) break; if (temp != aAccessible && temp->IsHyperText()) return temp->AsHyperText(); sibling = aBackward ? temp->PrevSibling() : temp->NextSibling(); if (sibling) break; } while ((temp = temp->Parent())); if (!sibling) break; accessible = sibling; if (accessible->IsHyperText()) return accessible->AsHyperText(); } return nullptr; }
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; } }
NS_IMETHODIMP nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult) { NS_ENSURE_ARG(aResult); *aResult = false; int32_t tempStart = mStartOffset, tempEnd = mEndOffset; Accessible* tempPosition = mPosition; Accessible* root = GetActiveRoot(); while (true) { Accessible* curPosition = tempPosition; HyperTextAccessible* text; // Find the nearest text node using a reverse preorder traversal starting // from the current node. if (!(text = tempPosition->AsHyperText())) { text = SearchForText(tempPosition, true); if (!text) return NS_OK; if (text != curPosition) tempStart = tempEnd = -1; tempPosition = text; } // If the search led to the parent of the node we started on (e.g. when // starting on a text leaf), start the text movement from the end of that // node, otherwise we just default to 0. if (tempStart == -1) { if (tempPosition != curPosition) tempStart = text == curPosition->Parent() ? text->GetChildOffset(curPosition) : text->CharacterCount(); else tempStart = 0; } // If there's no more text on the current node, try to find the previous // text node; if there isn't one, bail out. if (tempStart == 0) { if (tempPosition == root) return NS_OK; // If we're currently sitting on a link, try move to either the previous // sibling or the parent, whichever is closer to the current end // offset. Otherwise, do a forward search for the next node to land on // (we don't do this in the first case because we don't want to go to the // subtree). Accessible* sibling = tempPosition->PrevSibling(); if (tempPosition->IsLink()) { if (sibling && sibling->IsLink()) { HyperTextAccessible* siblingText = sibling->AsHyperText(); tempStart = tempEnd = siblingText ? siblingText->CharacterCount() : -1; tempPosition = sibling; } else { tempStart = tempPosition->StartOffset(); tempEnd = tempPosition->EndOffset(); tempPosition = tempPosition->Parent(); } } else { HyperTextAccessible* tempText = SearchForText(tempPosition, true); if (!tempText) return NS_OK; tempPosition = tempText; tempStart = tempEnd = tempText->CharacterCount(); } continue; } AccessibleTextBoundary startBoundary, endBoundary; switch (aBoundary) { case CHAR_BOUNDARY: startBoundary = nsIAccessibleText::BOUNDARY_CHAR; endBoundary = nsIAccessibleText::BOUNDARY_CHAR; break; case WORD_BOUNDARY: startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; break; default: return NS_ERROR_INVALID_ARG; } nsAutoString unusedText; int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0; text->TextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd, unusedText); if (newStart < tempStart) tempStart = newEnd >= currentStart ? newStart : newEnd; else // XXX: In certain odd cases newStart is equal to tempStart text->TextBeforeOffset(tempStart - 1, startBoundary, &newStart, &tempStart, unusedText); text->TextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd, unusedText); tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart; // The offset range we've obtained might have embedded characters in it, // limit the range to the start of the last occurrence of an embedded // character. Accessible* childAtOffset = nullptr; for (int32_t i = tempEnd - 1; i >= tempStart; i--) { childAtOffset = text->GetChildAtOffset(i); if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) { tempStart = childAtOffset->EndOffset(); break; } } // If there's an embedded character at the very end of the range, we // instead want to traverse into it. So restart the movement with // the child as the starting point. if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) && tempEnd == static_cast<int32_t>(childAtOffset->EndOffset())) { tempPosition = childAtOffset; tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount(); continue; } *aResult = true; Accessible* startPosition = mPosition; int32_t oldStart = mStartOffset, oldEnd = mEndOffset; mPosition = tempPosition; mStartOffset = tempStart; mEndOffset = tempEnd; NotifyOfPivotChange(startPosition, oldStart, oldEnd, nsIAccessiblePivot::REASON_TEXT); return NS_OK; } }