2019-09-19 01:23:58 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* 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 "Pivot.h"
|
|
|
|
|
|
|
|
#include "AccIterator.h"
|
2021-02-20 02:14:32 +03:00
|
|
|
#include "LocalAccessible.h"
|
2021-07-22 20:58:49 +03:00
|
|
|
#include "RemoteAccessible.h"
|
2020-05-01 07:28:35 +03:00
|
|
|
#include "nsAccUtils.h"
|
2023-06-29 00:45:16 +03:00
|
|
|
#include "nsIAccessiblePivot.h"
|
2019-09-19 01:23:58 +03:00
|
|
|
|
2022-02-15 08:35:14 +03:00
|
|
|
#include "mozilla/a11y/Accessible.h"
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
using namespace mozilla;
|
|
|
|
using namespace mozilla::a11y;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Pivot
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Pivot::Pivot(Accessible* aRoot) : mRoot(aRoot) { MOZ_COUNT_CTOR(Pivot); }
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
Pivot::~Pivot() { MOZ_COUNT_DTOR(Pivot); }
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::AdjustStartPosition(Accessible* aAnchor, PivotRule& aRule,
|
|
|
|
uint16_t* aFilterResult) {
|
|
|
|
Accessible* matched = aAnchor;
|
2019-09-19 01:23:58 +03:00
|
|
|
*aFilterResult = aRule.Match(aAnchor);
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (aAnchor && aAnchor != mRoot) {
|
|
|
|
for (Accessible* temp = aAnchor->Parent(); temp && temp != mRoot;
|
|
|
|
temp = temp->Parent()) {
|
2019-09-19 01:23:58 +03:00
|
|
|
uint16_t filtered = aRule.Match(temp);
|
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
|
|
|
|
*aFilterResult = filtered;
|
|
|
|
matched = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::SearchBackward(Accessible* aAnchor, PivotRule& aRule,
|
|
|
|
bool aSearchCurrent) {
|
|
|
|
// Initial position could be unset, in that case return null.
|
|
|
|
if (!aAnchor) {
|
|
|
|
return nullptr;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* acc = AdjustStartPosition(aAnchor, aRule, &filtered);
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
while (acc && acc != mRoot) {
|
|
|
|
Accessible* parent = acc->Parent();
|
2022-11-29 23:31:21 +03:00
|
|
|
#if defined(ANDROID)
|
|
|
|
MOZ_ASSERT(
|
|
|
|
acc->IsLocal() || (acc->IsRemote() && parent->IsRemote()),
|
|
|
|
"Pivot::SearchBackward climbed out of remote subtree in Android!");
|
|
|
|
#endif
|
2021-07-22 20:58:49 +03:00
|
|
|
int32_t idxInParent = acc->IndexInParent();
|
|
|
|
while (idxInParent > 0 && parent) {
|
|
|
|
acc = parent->ChildAt(--idxInParent);
|
|
|
|
if (!acc) {
|
2019-09-19 01:23:58 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
filtered = aRule.Match(acc);
|
2019-09-19 01:23:58 +03:00
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* lastChild = acc->LastChild();
|
2019-09-19 01:23:58 +03:00
|
|
|
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
|
2021-07-22 20:58:49 +03:00
|
|
|
lastChild) {
|
|
|
|
parent = acc;
|
|
|
|
acc = lastChild;
|
|
|
|
idxInParent = acc->IndexInParent();
|
|
|
|
filtered = aRule.Match(acc);
|
|
|
|
lastChild = acc->LastChild();
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
acc = parent;
|
|
|
|
if (!acc) {
|
2019-09-19 01:23:58 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
filtered = aRule.Match(acc);
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
return nullptr;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::SearchForward(Accessible* aAnchor, PivotRule& aRule,
|
|
|
|
bool aSearchCurrent) {
|
2019-09-19 01:23:58 +03:00
|
|
|
// Initial position could be not set, in that case begin search from root.
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* acc = aAnchor ? aAnchor : mRoot;
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
2021-07-22 20:58:49 +03:00
|
|
|
acc = AdjustStartPosition(acc, aRule, &filtered);
|
2019-09-19 01:23:58 +03:00
|
|
|
if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
while (acc) {
|
|
|
|
Accessible* firstChild = acc->FirstChild();
|
2019-09-19 01:23:58 +03:00
|
|
|
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
|
2021-07-22 20:58:49 +03:00
|
|
|
firstChild) {
|
|
|
|
acc = firstChild;
|
|
|
|
filtered = aRule.Match(acc);
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
2021-07-22 20:58:49 +03:00
|
|
|
firstChild = acc->FirstChild();
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* sibling = nullptr;
|
|
|
|
Accessible* temp = acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
do {
|
|
|
|
if (temp == mRoot) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
sibling = temp->NextSibling();
|
2019-09-19 01:23:58 +03:00
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (sibling) {
|
2019-09-19 01:23:58 +03:00
|
|
|
break;
|
|
|
|
}
|
2021-07-22 20:58:49 +03:00
|
|
|
temp = temp->Parent();
|
2022-11-29 23:31:21 +03:00
|
|
|
#if defined(ANDROID)
|
|
|
|
MOZ_ASSERT(
|
|
|
|
acc->IsLocal() || (acc->IsRemote() && temp->IsRemote()),
|
|
|
|
"Pivot::SearchForward climbed out of remote subtree in Android!");
|
|
|
|
#endif
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
} while (temp);
|
2019-09-19 01:23:58 +03:00
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (!sibling) {
|
2019-09-19 01:23:58 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
acc = sibling;
|
|
|
|
filtered = aRule.Match(acc);
|
2019-09-19 01:23:58 +03:00
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) {
|
2021-07-22 20:58:49 +03:00
|
|
|
return acc;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
return nullptr;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::Next(Accessible* aAnchor, PivotRule& aRule,
|
|
|
|
bool aIncludeStart) {
|
2019-09-19 01:23:58 +03:00
|
|
|
return SearchForward(aAnchor, aRule, aIncludeStart);
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::Prev(Accessible* aAnchor, PivotRule& aRule,
|
|
|
|
bool aIncludeStart) {
|
2019-09-19 01:23:58 +03:00
|
|
|
return SearchBackward(aAnchor, aRule, aIncludeStart);
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::First(PivotRule& aRule) {
|
2019-09-19 01:23:58 +03:00
|
|
|
return SearchForward(mRoot, aRule, true);
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::Last(PivotRule& aRule) {
|
|
|
|
Accessible* lastAcc = mRoot;
|
2019-09-19 01:23:58 +03:00
|
|
|
|
|
|
|
// First go to the last accessible in pre-order
|
2021-07-22 20:58:49 +03:00
|
|
|
while (lastAcc && lastAcc->HasChildren()) {
|
|
|
|
lastAcc = lastAcc->LastChild();
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Search backwards from last accessible and find the last occurrence in the
|
|
|
|
// doc
|
2021-07-22 20:58:49 +03:00
|
|
|
return SearchBackward(lastAcc, aRule, true);
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* Pivot::AtPoint(int32_t aX, int32_t aY, PivotRule& aRule) {
|
|
|
|
Accessible* match = nullptr;
|
|
|
|
Accessible* child =
|
|
|
|
mRoot ? mRoot->ChildAtPoint(aX, aY,
|
|
|
|
Accessible::EWhichChildAtPoint::DeepestChild)
|
|
|
|
: nullptr;
|
|
|
|
while (child && (mRoot != child)) {
|
2019-09-19 01:23:58 +03:00
|
|
|
uint16_t filtered = aRule.Match(child);
|
|
|
|
|
|
|
|
// Ignore any matching nodes that were below this one
|
|
|
|
if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
|
2021-07-22 20:58:49 +03:00
|
|
|
match = nullptr;
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match if no node below this is a match
|
2021-07-22 20:58:49 +03:00
|
|
|
if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) {
|
2022-01-19 02:31:21 +03:00
|
|
|
LayoutDeviceIntRect childRect = child->IsLocal()
|
|
|
|
? child->AsLocal()->Bounds()
|
|
|
|
: child->AsRemote()->Bounds();
|
2019-09-19 01:23:58 +03:00
|
|
|
// Double-check child's bounds since the deepest child may have been out
|
|
|
|
// of bounds. This assures we don't return a false positive.
|
|
|
|
if (childRect.Contains(aX, aY)) {
|
|
|
|
match = child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
child = child->Parent();
|
2019-09-19 01:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
2020-05-29 20:37:46 +03:00
|
|
|
|
|
|
|
// Role Rule
|
|
|
|
|
2020-08-05 00:00:25 +03:00
|
|
|
PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole)
|
|
|
|
: mRole(aRole), mDirectDescendantsFrom(nullptr) {}
|
|
|
|
|
|
|
|
PivotRoleRule::PivotRoleRule(mozilla::a11y::role aRole,
|
2021-07-22 20:58:49 +03:00
|
|
|
Accessible* aDirectDescendantsFrom)
|
2020-08-05 00:00:25 +03:00
|
|
|
: mRole(aRole), mDirectDescendantsFrom(aDirectDescendantsFrom) {}
|
2020-05-29 20:37:46 +03:00
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
uint16_t PivotRoleRule::Match(Accessible* aAcc) {
|
2020-05-29 20:37:46 +03:00
|
|
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (nsAccUtils::MustPrune(aAcc)) {
|
2020-05-29 20:37:46 +03:00
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (mDirectDescendantsFrom && (aAcc != mDirectDescendantsFrom)) {
|
2020-08-05 00:00:25 +03:00
|
|
|
// If we've specified mDirectDescendantsFrom, we should ignore
|
|
|
|
// non-direct descendants of from the specified AoP. Because
|
2021-07-22 20:58:49 +03:00
|
|
|
// pivot performs a preorder traversal, the first aAcc
|
2020-08-05 00:00:25 +03:00
|
|
|
// object(s) that don't equal mDirectDescendantsFrom will be
|
|
|
|
// mDirectDescendantsFrom's children. We'll process them, but ignore
|
|
|
|
// their subtrees thereby processing direct descendants of
|
|
|
|
// mDirectDescendantsFrom only.
|
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
if (aAcc && aAcc->Role() == mRole) {
|
2020-07-29 01:35:24 +03:00
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2021-01-25 04:12:17 +03:00
|
|
|
|
2022-02-16 21:42:53 +03:00
|
|
|
// State Rule
|
|
|
|
|
|
|
|
PivotStateRule::PivotStateRule(uint64_t aState) : mState(aState) {}
|
|
|
|
|
|
|
|
uint16_t PivotStateRule::Match(Accessible* aAcc) {
|
|
|
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
|
|
|
|
|
|
if (nsAccUtils::MustPrune(aAcc)) {
|
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aAcc && (aAcc->State() & mState)) {
|
|
|
|
result = nsIAccessibleTraversalRule::FILTER_MATCH |
|
|
|
|
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-01-25 04:12:17 +03:00
|
|
|
// LocalAccInSameDocRule
|
|
|
|
|
2021-07-22 20:58:49 +03:00
|
|
|
uint16_t LocalAccInSameDocRule::Match(Accessible* aAcc) {
|
|
|
|
LocalAccessible* acc = aAcc ? aAcc->AsLocal() : nullptr;
|
2021-01-25 04:12:17 +03:00
|
|
|
if (!acc) {
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
if (acc->IsOuterDoc()) {
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_MATCH |
|
|
|
|
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
|
|
}
|
2022-10-27 23:32:18 +03:00
|
|
|
|
|
|
|
// Radio Button Name Rule
|
|
|
|
|
|
|
|
PivotRadioNameRule::PivotRadioNameRule(const nsString& aName) : mName(aName) {}
|
|
|
|
|
|
|
|
uint16_t PivotRadioNameRule::Match(Accessible* aAcc) {
|
|
|
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
|
|
RemoteAccessible* remote = aAcc->AsRemote();
|
2023-05-29 02:42:12 +03:00
|
|
|
if (!remote) {
|
2022-10-27 23:32:18 +03:00
|
|
|
// We need the cache to be able to fetch the name attribute below.
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsAccUtils::MustPrune(aAcc) || aAcc->IsOuterDoc()) {
|
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remote->IsHTMLRadioButton()) {
|
2022-10-27 23:32:18 +03:00
|
|
|
nsString currName = remote->GetCachedHTMLNameAttribute();
|
2022-10-27 23:32:18 +03:00
|
|
|
if (!currName.IsEmpty() && mName.Equals(currName)) {
|
|
|
|
result |= nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2022-10-27 23:32:18 +03:00
|
|
|
|
|
|
|
// MustPruneSameDocRule
|
|
|
|
|
|
|
|
uint16_t MustPruneSameDocRule::Match(Accessible* aAcc) {
|
|
|
|
if (!aAcc) {
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsAccUtils::MustPrune(aAcc) || aAcc->IsOuterDoc()) {
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_MATCH |
|
|
|
|
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsIAccessibleTraversalRule::FILTER_MATCH;
|
|
|
|
}
|