зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 8be4693aecbb (bug 1754905) for causing bustages in xpcAccessiblePivot.h. CLOSED TREE
This commit is contained in:
Родитель
3f23020c5c
Коммит
f0967c3305
|
@ -20,7 +20,6 @@
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
#include "nsIAccessibleAnnouncementEvent.h"
|
#include "nsIAccessibleAnnouncementEvent.h"
|
||||||
#include "nsIAccessiblePivot.h"
|
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
#include "nsTextEquivUtils.h"
|
#include "nsTextEquivUtils.h"
|
||||||
#include "nsWhitespaceTokenizer.h"
|
#include "nsWhitespaceTokenizer.h"
|
||||||
|
@ -134,8 +133,11 @@ nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
|
||||||
RefPtr<AccessibleWrap> newPosition =
|
RefPtr<AccessibleWrap> newPosition =
|
||||||
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
|
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
|
||||||
if (sessionAcc && newPosition) {
|
if (sessionAcc && newPosition) {
|
||||||
MOZ_ASSERT(vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT);
|
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
|
||||||
sessionAcc->SendHoverEnterEvent(newPosition);
|
sessionAcc->SendHoverEnterEvent(newPosition);
|
||||||
|
} else {
|
||||||
|
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,8 +182,11 @@ void a11y::ProxyVirtualCursorChangeEvent(RemoteAccessible* aTarget,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(aReason == nsIAccessiblePivot::REASON_POINT);
|
if (aReason == nsIAccessiblePivot::REASON_POINT) {
|
||||||
sessionAcc->SendHoverEnterEvent(aNewPosition);
|
sessionAcc->SendHoverEnterEvent(aNewPosition);
|
||||||
|
} else {
|
||||||
|
sessionAcc->SendAccessibilityFocusedEvent(aNewPosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void a11y::ProxyScrollingEvent(RemoteAccessible* aTarget, uint32_t aEventType,
|
void a11y::ProxyScrollingEvent(RemoteAccessible* aTarget, uint32_t aEventType,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include "RemoteAccessible.h"
|
#include "RemoteAccessible.h"
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
#include "nsIAccessiblePivot.h"
|
|
||||||
|
|
||||||
#include "mozilla/a11y/Accessible.h"
|
#include "mozilla/a11y/Accessible.h"
|
||||||
#include "mozilla/a11y/HyperTextAccessibleBase.h"
|
#include "mozilla/a11y/HyperTextAccessibleBase.h"
|
||||||
|
|
|
@ -43,6 +43,7 @@ UNIFIED_SOURCES += [
|
||||||
"FocusManager.cpp",
|
"FocusManager.cpp",
|
||||||
"NotificationController.cpp",
|
"NotificationController.cpp",
|
||||||
"nsAccessibilityService.cpp",
|
"nsAccessibilityService.cpp",
|
||||||
|
"nsAccessiblePivot.cpp",
|
||||||
"nsAccUtils.cpp",
|
"nsAccUtils.cpp",
|
||||||
"nsCoreUtils.cpp",
|
"nsCoreUtils.cpp",
|
||||||
"nsEventShell.cpp",
|
"nsEventShell.cpp",
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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 "nsAccessiblePivot.h"
|
||||||
|
|
||||||
|
#include "HyperTextAccessible.h"
|
||||||
|
#include "nsAccUtils.h"
|
||||||
|
#include "States.h"
|
||||||
|
#include "Pivot.h"
|
||||||
|
#include "xpcAccessibleDocument.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
|
using namespace mozilla::a11y;
|
||||||
|
using mozilla::DebugOnly;
|
||||||
|
using mozilla::Maybe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that stores a given traversal rule during the pivot movement.
|
||||||
|
*/
|
||||||
|
class RuleCache : public PivotRule {
|
||||||
|
public:
|
||||||
|
explicit RuleCache(nsIAccessibleTraversalRule* aRule)
|
||||||
|
: mRule(aRule), mPreFilter{0} {}
|
||||||
|
~RuleCache() {}
|
||||||
|
|
||||||
|
virtual uint16_t Match(Accessible* aAcc) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
|
||||||
|
Maybe<nsTArray<uint32_t>> mAcceptRoles;
|
||||||
|
uint32_t mPreFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// nsAccessiblePivot
|
||||||
|
|
||||||
|
nsAccessiblePivot::nsAccessiblePivot(LocalAccessible* aRoot)
|
||||||
|
: mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr) {
|
||||||
|
NS_ASSERTION(aRoot, "A root accessible is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAccessiblePivot::~nsAccessiblePivot() {}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// nsISupports
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers)
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
|
||||||
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// nsIAccessiblePivot
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::GetRoot(nsIAccessible** aRoot) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aRoot);
|
||||||
|
|
||||||
|
NS_IF_ADDREF(*aRoot = ToXPC(mRoot));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::GetPosition(nsIAccessible** aPosition) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aPosition);
|
||||||
|
|
||||||
|
NS_IF_ADDREF(*aPosition = ToXPC(mPosition));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) {
|
||||||
|
RefPtr<LocalAccessible> position = nullptr;
|
||||||
|
|
||||||
|
if (aPosition) {
|
||||||
|
position = aPosition->ToInternalAccessible();
|
||||||
|
if (!position || !IsDescendantOf(position, GetActiveRoot())) {
|
||||||
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap old position with new position, saves us an AddRef/Release.
|
||||||
|
mPosition.swap(position);
|
||||||
|
NotifyOfPivotChange(position, nsIAccessiblePivot::REASON_NONE, false);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aModalRoot);
|
||||||
|
|
||||||
|
NS_IF_ADDREF(*aModalRoot = ToXPC(mModalRoot));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot) {
|
||||||
|
LocalAccessible* modalRoot = nullptr;
|
||||||
|
|
||||||
|
if (aModalRoot) {
|
||||||
|
modalRoot = aModalRoot->ToInternalAccessible();
|
||||||
|
if (!modalRoot || !IsDescendantOf(modalRoot, mRoot)) {
|
||||||
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mModalRoot = modalRoot;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traversal functions
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
|
||||||
|
nsIAccessible* aAnchor, bool aIncludeStart,
|
||||||
|
bool aIsFromUserInput, uint8_t aArgc,
|
||||||
|
bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
NS_ENSURE_ARG(aRule);
|
||||||
|
*aResult = false;
|
||||||
|
|
||||||
|
LocalAccessible* anchor = mPosition;
|
||||||
|
if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
|
||||||
|
|
||||||
|
if (anchor &&
|
||||||
|
(anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot()))) {
|
||||||
|
return NS_ERROR_NOT_IN_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pivot pivot(GetActiveRoot());
|
||||||
|
RuleCache rule(aRule);
|
||||||
|
Accessible* newPos =
|
||||||
|
pivot.Next(anchor, rule, (aArgc > 1) ? aIncludeStart : false);
|
||||||
|
if (newPos && newPos->IsLocal()) {
|
||||||
|
*aResult =
|
||||||
|
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_NEXT,
|
||||||
|
(aArgc > 2) ? aIsFromUserInput : true);
|
||||||
|
} else if (newPos && newPos->IsRemote()) {
|
||||||
|
// we shouldn't ever end up with a proxy here, but if we do for some
|
||||||
|
// reason something is wrong. we should still return OK if we're null
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
|
||||||
|
nsIAccessible* aAnchor, bool aIncludeStart,
|
||||||
|
bool aIsFromUserInput, uint8_t aArgc,
|
||||||
|
bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
NS_ENSURE_ARG(aRule);
|
||||||
|
*aResult = false;
|
||||||
|
|
||||||
|
LocalAccessible* anchor = mPosition;
|
||||||
|
if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
|
||||||
|
|
||||||
|
if (anchor &&
|
||||||
|
(anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot()))) {
|
||||||
|
return NS_ERROR_NOT_IN_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pivot pivot(GetActiveRoot());
|
||||||
|
RuleCache rule(aRule);
|
||||||
|
Accessible* newPos =
|
||||||
|
pivot.Prev(anchor, rule, (aArgc > 1) ? aIncludeStart : false);
|
||||||
|
if (newPos && newPos->IsLocal()) {
|
||||||
|
*aResult =
|
||||||
|
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_PREV,
|
||||||
|
(aArgc > 2) ? aIsFromUserInput : true);
|
||||||
|
} else if (newPos && newPos->IsRemote()) {
|
||||||
|
// we shouldn't ever end up with a proxy here, but if we do for some
|
||||||
|
// reason something is wrong. we should still return OK if we're null
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule,
|
||||||
|
bool aIsFromUserInput, uint8_t aArgc,
|
||||||
|
bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
NS_ENSURE_ARG(aRule);
|
||||||
|
|
||||||
|
LocalAccessible* root = GetActiveRoot();
|
||||||
|
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
|
||||||
|
|
||||||
|
Pivot pivot(GetActiveRoot());
|
||||||
|
RuleCache rule(aRule);
|
||||||
|
Accessible* newPos = pivot.First(rule);
|
||||||
|
if (newPos && newPos->IsLocal()) {
|
||||||
|
*aResult =
|
||||||
|
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_FIRST,
|
||||||
|
(aArgc > 0) ? aIsFromUserInput : true);
|
||||||
|
} else if (newPos && newPos->IsRemote()) {
|
||||||
|
// we shouldn't ever end up with a proxy here, but if we do for some
|
||||||
|
// reason something is wrong. we should still return OK if we're null
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
|
||||||
|
bool aIsFromUserInput, uint8_t aArgc,
|
||||||
|
bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
NS_ENSURE_ARG(aRule);
|
||||||
|
|
||||||
|
LocalAccessible* root = GetActiveRoot();
|
||||||
|
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
|
||||||
|
|
||||||
|
Pivot pivot(root);
|
||||||
|
RuleCache rule(aRule);
|
||||||
|
Accessible* newPos = pivot.Last(rule);
|
||||||
|
if (newPos && newPos->IsLocal()) {
|
||||||
|
*aResult =
|
||||||
|
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_LAST,
|
||||||
|
(aArgc > 0) ? aIsFromUserInput : true);
|
||||||
|
} else if (newPos && newPos->IsRemote()) {
|
||||||
|
// we shouldn't ever end up with a proxy here, but if we do for some
|
||||||
|
// reason something is wrong. we should still return OK if we're null
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, int32_t aX,
|
||||||
|
int32_t aY, bool aIgnoreNoMatch,
|
||||||
|
bool aIsFromUserInput, uint8_t aArgc,
|
||||||
|
bool* aResult) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aResult);
|
||||||
|
NS_ENSURE_ARG_POINTER(aRule);
|
||||||
|
|
||||||
|
*aResult = false;
|
||||||
|
|
||||||
|
LocalAccessible* root = GetActiveRoot();
|
||||||
|
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
|
||||||
|
|
||||||
|
RuleCache rule(aRule);
|
||||||
|
Pivot pivot(root);
|
||||||
|
|
||||||
|
Accessible* newPos = pivot.AtPoint(aX, aY, rule);
|
||||||
|
if ((newPos && newPos->IsLocal()) ||
|
||||||
|
!aIgnoreNoMatch) { // TODO does this need a proxy check?
|
||||||
|
*aResult = MovePivotInternal(newPos ? newPos->AsLocal() : nullptr,
|
||||||
|
nsIAccessiblePivot::REASON_POINT,
|
||||||
|
(aArgc > 0) ? aIsFromUserInput : true);
|
||||||
|
} else if (newPos && newPos->IsRemote()) {
|
||||||
|
// we shouldn't ever end up with a proxy here, but if we do for some
|
||||||
|
// reason something is wrong. we should still return OK if we're null
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observer functions
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver) {
|
||||||
|
NS_ENSURE_ARG(aObserver);
|
||||||
|
|
||||||
|
mObservers.AppendElement(aObserver);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver) {
|
||||||
|
NS_ENSURE_ARG(aObserver);
|
||||||
|
|
||||||
|
return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private utility methods
|
||||||
|
|
||||||
|
bool nsAccessiblePivot::IsDescendantOf(LocalAccessible* aAccessible,
|
||||||
|
LocalAccessible* aAncestor) {
|
||||||
|
if (!aAncestor || aAncestor->IsDefunct()) return false;
|
||||||
|
|
||||||
|
// XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
|
||||||
|
LocalAccessible* accessible = aAccessible;
|
||||||
|
do {
|
||||||
|
if (accessible == aAncestor) return true;
|
||||||
|
} while ((accessible = accessible->LocalParent()));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nsAccessiblePivot::MovePivotInternal(LocalAccessible* aPosition,
|
||||||
|
PivotMoveReason aReason,
|
||||||
|
bool aIsFromUserInput) {
|
||||||
|
RefPtr<LocalAccessible> oldPosition = std::move(mPosition);
|
||||||
|
mPosition = aPosition;
|
||||||
|
|
||||||
|
return NotifyOfPivotChange(oldPosition, aReason, aIsFromUserInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nsAccessiblePivot::NotifyOfPivotChange(LocalAccessible* aOldPosition,
|
||||||
|
int16_t aReason,
|
||||||
|
bool aIsFromUserInput) {
|
||||||
|
if (aOldPosition == mPosition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAccessible> xpcOldPos = ToXPC(aOldPosition); // death grip
|
||||||
|
for (nsIAccessiblePivotObserver* obs : mObservers.ForwardRange()) {
|
||||||
|
obs->OnPivotChanged(this, xpcOldPos, ToXPC(mPosition), aReason,
|
||||||
|
aIsFromUserInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t RuleCache::Match(Accessible* aAcc) {
|
||||||
|
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||||
|
|
||||||
|
if (!mAcceptRoles) {
|
||||||
|
mAcceptRoles.emplace();
|
||||||
|
DebugOnly<nsresult> rv = mRule->GetMatchRoles(*mAcceptRoles);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
rv = mRule->GetPreFilter(&mPreFilter);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPreFilter) {
|
||||||
|
uint64_t state = aAcc->State();
|
||||||
|
|
||||||
|
if ((nsIAccessibleTraversalRule::PREFILTER_PLATFORM_PRUNED & mPreFilter) &&
|
||||||
|
nsAccUtils::MustPrune(aAcc)) {
|
||||||
|
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
|
||||||
|
(state & states::INVISIBLE)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
|
||||||
|
(state & states::OFFSCREEN)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
|
||||||
|
!(state & states::FOCUSABLE)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) {
|
||||||
|
if (aAcc->Opacity() == 0.0f) {
|
||||||
|
return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAcceptRoles->Length() > 0) {
|
||||||
|
uint32_t accessibleRole = aAcc->Role();
|
||||||
|
bool matchesRole = false;
|
||||||
|
for (uint32_t idx = 0; idx < mAcceptRoles->Length(); idx++) {
|
||||||
|
matchesRole = mAcceptRoles->ElementAt(idx) == accessibleRole;
|
||||||
|
if (matchesRole) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchesRole) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||||
|
|
||||||
|
DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
return result | matchResult;
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef _nsAccessiblePivot_H_
|
||||||
|
#define _nsAccessiblePivot_H_
|
||||||
|
|
||||||
|
#include "nsIAccessiblePivot.h"
|
||||||
|
|
||||||
|
#include "LocalAccessible-inl.h"
|
||||||
|
#include "nsTObserverArray.h"
|
||||||
|
#include "nsCycleCollectionParticipant.h"
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
|
||||||
|
class RuleCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class represents an accessible pivot.
|
||||||
|
*/
|
||||||
|
class nsAccessiblePivot final : public nsIAccessiblePivot {
|
||||||
|
public:
|
||||||
|
typedef mozilla::a11y::LocalAccessible LocalAccessible;
|
||||||
|
|
||||||
|
explicit nsAccessiblePivot(LocalAccessible* aRoot);
|
||||||
|
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot,
|
||||||
|
nsIAccessiblePivot)
|
||||||
|
|
||||||
|
NS_DECL_NSIACCESSIBLEPIVOT
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple getter for the pivot's position.
|
||||||
|
*/
|
||||||
|
LocalAccessible* Position() { return mPosition; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
~nsAccessiblePivot();
|
||||||
|
nsAccessiblePivot() = delete;
|
||||||
|
nsAccessiblePivot(const nsAccessiblePivot&) = delete;
|
||||||
|
void operator=(const nsAccessiblePivot&) = delete;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify all observers on a pivot change. Return true if it has changed and
|
||||||
|
* observers have been notified.
|
||||||
|
*/
|
||||||
|
bool NotifyOfPivotChange(LocalAccessible* aOldPosition,
|
||||||
|
PivotMoveReason aReason, bool aIsFromUserInput);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see that the given accessible is a descendant of given ancestor
|
||||||
|
*/
|
||||||
|
bool IsDescendantOf(LocalAccessible* aAccessible, LocalAccessible* aAncestor);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search in preorder for the first accessible to match the rule.
|
||||||
|
*/
|
||||||
|
LocalAccessible* SearchForward(LocalAccessible* aAccessible,
|
||||||
|
nsIAccessibleTraversalRule* aRule,
|
||||||
|
bool aSearchCurrent, nsresult* aResult);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reverse search in preorder for the first accessible to match the rule.
|
||||||
|
*/
|
||||||
|
LocalAccessible* SearchBackward(LocalAccessible* aAccessible,
|
||||||
|
nsIAccessibleTraversalRule* aRule,
|
||||||
|
bool aSearchCurrent, nsresult* aResult);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the effective root for this pivot, either the true root or modal root.
|
||||||
|
*/
|
||||||
|
LocalAccessible* GetActiveRoot() const {
|
||||||
|
if (mModalRoot) {
|
||||||
|
NS_ENSURE_FALSE(mModalRoot->IsDefunct(), mRoot);
|
||||||
|
return mModalRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the pivot, and notify observers. Return true if it moved.
|
||||||
|
*/
|
||||||
|
bool MovePivotInternal(LocalAccessible* aPosition, PivotMoveReason aReason,
|
||||||
|
bool aIsFromUserInput);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get initial node we should start a search from with a given rule.
|
||||||
|
*
|
||||||
|
* When we do a move operation from one position to another,
|
||||||
|
* the initial position can be inside of a subtree that is ignored by
|
||||||
|
* the given rule. We need to step out of the ignored subtree and start
|
||||||
|
* the search from there.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
LocalAccessible* AdjustStartPosition(LocalAccessible* aAccessible,
|
||||||
|
RuleCache& aCache,
|
||||||
|
uint16_t* aFilterResult,
|
||||||
|
nsresult* aResult);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The root accessible.
|
||||||
|
*/
|
||||||
|
RefPtr<LocalAccessible> mRoot;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The temporary modal root accessible.
|
||||||
|
*/
|
||||||
|
RefPtr<LocalAccessible> mModalRoot;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The current pivot position.
|
||||||
|
*/
|
||||||
|
RefPtr<LocalAccessible> mPosition;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list of pivot-changed observers.
|
||||||
|
*/
|
||||||
|
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -464,11 +464,7 @@ void Accessible::DebugDescription(nsCString& aDesc) const {
|
||||||
void Accessible::DebugPrint(const char* aPrefix,
|
void Accessible::DebugPrint(const char* aPrefix,
|
||||||
const Accessible* aAccessible) {
|
const Accessible* aAccessible) {
|
||||||
nsAutoCString desc;
|
nsAutoCString desc;
|
||||||
if (aAccessible) {
|
aAccessible->DebugDescription(desc);
|
||||||
aAccessible->DebugDescription(desc);
|
|
||||||
} else {
|
|
||||||
desc.AssignLiteral("[null]");
|
|
||||||
}
|
|
||||||
# if defined(ANDROID)
|
# if defined(ANDROID)
|
||||||
printf_stderr("%s %s\n", aPrefix, desc.get());
|
printf_stderr("%s %s\n", aPrefix, desc.get());
|
||||||
# else
|
# else
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
|
#include "nsAccessiblePivot.h"
|
||||||
#include "NotificationController.h"
|
#include "NotificationController.h"
|
||||||
#include "States.h"
|
#include "States.h"
|
||||||
#include "nsIScrollableFrame.h"
|
#include "nsIScrollableFrame.h"
|
||||||
|
@ -33,6 +34,14 @@ inline LocalAccessible* DocAccessible::AccessibleOrTrueContainer(
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline nsIAccessiblePivot* DocAccessible::VirtualCursor() {
|
||||||
|
if (!mVirtualCursor) {
|
||||||
|
mVirtualCursor = new nsAccessiblePivot(this);
|
||||||
|
mVirtualCursor->AddObserver(this);
|
||||||
|
}
|
||||||
|
return mVirtualCursor;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool DocAccessible::IsContentLoaded() const {
|
inline bool DocAccessible::IsContentLoaded() const {
|
||||||
// eDOMLoaded flag check is used for error pages as workaround to make this
|
// eDOMLoaded flag check is used for error pages as workaround to make this
|
||||||
// method return correct result since error pages do not receive 'pageshow'
|
// method return correct result since error pages do not receive 'pageshow'
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "HTMLImageMapAccessible.h"
|
#include "HTMLImageMapAccessible.h"
|
||||||
#include "mozilla/ProfilerMarkers.h"
|
#include "mozilla/ProfilerMarkers.h"
|
||||||
#include "nsAccCache.h"
|
#include "nsAccCache.h"
|
||||||
|
#include "nsAccessiblePivot.h"
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
#include "nsIIOService.h"
|
#include "nsIIOService.h"
|
||||||
|
@ -98,6 +99,7 @@ DocAccessible::DocAccessible(dom::Document* aDocument,
|
||||||
mViewportCacheDirty(false),
|
mViewportCacheDirty(false),
|
||||||
mLoadEventType(0),
|
mLoadEventType(0),
|
||||||
mPrevStateBits(0),
|
mPrevStateBits(0),
|
||||||
|
mVirtualCursor(nullptr),
|
||||||
mPresShell(aPresShell),
|
mPresShell(aPresShell),
|
||||||
mIPCDoc(nullptr) {
|
mIPCDoc(nullptr) {
|
||||||
mGenericTypes |= eDocument;
|
mGenericTypes |= eDocument;
|
||||||
|
@ -120,6 +122,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible,
|
||||||
LocalAccessible)
|
LocalAccessible)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
|
||||||
for (const auto& hashEntry : tmp->mDependentIDsHashes.Values()) {
|
for (const auto& hashEntry : tmp->mDependentIDsHashes.Values()) {
|
||||||
for (const auto& providers : hashEntry->Values()) {
|
for (const auto& providers : hashEntry->Values()) {
|
||||||
|
@ -146,6 +149,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, LocalAccessible)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, LocalAccessible)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
|
||||||
tmp->mDependentIDsHashes.Clear();
|
tmp->mDependentIDsHashes.Clear();
|
||||||
tmp->mNodeToAccessibleMap.Clear();
|
tmp->mNodeToAccessibleMap.Clear();
|
||||||
|
@ -161,6 +165,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocAccessible)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(HyperTextAccessible)
|
NS_INTERFACE_MAP_END_INHERITING(HyperTextAccessible)
|
||||||
|
|
||||||
NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
|
NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
|
||||||
|
@ -496,6 +501,11 @@ void DocAccessible::Shutdown() {
|
||||||
MOZ_ASSERT(!mIPCDoc);
|
MOZ_ASSERT(!mIPCDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mVirtualCursor) {
|
||||||
|
mVirtualCursor->RemoveObserver(this);
|
||||||
|
mVirtualCursor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
mDependentIDsHashes.Clear();
|
mDependentIDsHashes.Clear();
|
||||||
mNodeToAccessibleMap.Clear();
|
mNodeToAccessibleMap.Clear();
|
||||||
|
|
||||||
|
@ -713,6 +723,23 @@ std::pair<nsPoint, nsRect> DocAccessible::ComputeScrollData(
|
||||||
return {scrollPoint, scrollRange};
|
return {scrollPoint, scrollRange};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// nsIAccessiblePivotObserver
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
|
||||||
|
nsIAccessible* aOldAccessible,
|
||||||
|
nsIAccessible* aNewAccessible,
|
||||||
|
PivotMoveReason aReason, bool aIsFromUserInput) {
|
||||||
|
RefPtr<AccEvent> event = new AccVCChangeEvent(
|
||||||
|
this, (aOldAccessible ? aOldAccessible->ToInternalAccessible() : nullptr),
|
||||||
|
(aNewAccessible ? aNewAccessible->ToInternalAccessible() : nullptr),
|
||||||
|
aReason, aIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||||
|
nsEventShell::FireEvent(event);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// nsIDocumentObserver
|
// nsIDocumentObserver
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#ifndef mozilla_a11y_DocAccessible_h__
|
#ifndef mozilla_a11y_DocAccessible_h__
|
||||||
#define mozilla_a11y_DocAccessible_h__
|
#define mozilla_a11y_DocAccessible_h__
|
||||||
|
|
||||||
|
#include "nsIAccessiblePivot.h"
|
||||||
|
|
||||||
#include "HyperTextAccessibleWrap.h"
|
#include "HyperTextAccessibleWrap.h"
|
||||||
#include "AccEvent.h"
|
#include "AccEvent.h"
|
||||||
|
|
||||||
|
@ -17,6 +19,8 @@
|
||||||
#include "nsTHashSet.h"
|
#include "nsTHashSet.h"
|
||||||
#include "nsWeakReference.h"
|
#include "nsWeakReference.h"
|
||||||
|
|
||||||
|
class nsAccessiblePivot;
|
||||||
|
|
||||||
const uint32_t kDefaultCacheLength = 128;
|
const uint32_t kDefaultCacheLength = 128;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
@ -44,10 +48,13 @@ class TNotification;
|
||||||
*/
|
*/
|
||||||
class DocAccessible : public HyperTextAccessibleWrap,
|
class DocAccessible : public HyperTextAccessibleWrap,
|
||||||
public nsIDocumentObserver,
|
public nsIDocumentObserver,
|
||||||
public nsSupportsWeakReference {
|
public nsSupportsWeakReference,
|
||||||
|
public nsIAccessiblePivotObserver {
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible)
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible)
|
||||||
|
|
||||||
|
NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef mozilla::dom::Document Document;
|
typedef mozilla::dom::Document Document;
|
||||||
|
|
||||||
|
@ -128,6 +135,11 @@ class DocAccessible : public HyperTextAccessibleWrap,
|
||||||
*/
|
*/
|
||||||
void QueueCacheUpdateForDependentRelations(LocalAccessible* aAcc);
|
void QueueCacheUpdateForDependentRelations(LocalAccessible* aAcc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return virtual cursor associated with the document.
|
||||||
|
*/
|
||||||
|
nsIAccessiblePivot* VirtualCursor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the instance has shutdown.
|
* Returns true if the instance has shutdown.
|
||||||
*/
|
*/
|
||||||
|
@ -694,6 +706,11 @@ class DocAccessible : public HyperTextAccessibleWrap,
|
||||||
|
|
||||||
nsTArray<RefPtr<DocAccessible>> mChildDocuments;
|
nsTArray<RefPtr<DocAccessible>> mChildDocuments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The virtual cursor of the document.
|
||||||
|
*/
|
||||||
|
RefPtr<nsAccessiblePivot> mVirtualCursor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A storage class for pairing content with one of its relation attributes.
|
* A storage class for pairing content with one of its relation attributes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "HyperTextAccessible-inl.h"
|
#include "HyperTextAccessible-inl.h"
|
||||||
|
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
|
#include "nsAccessiblePivot.h"
|
||||||
#include "nsIAccessibleTypes.h"
|
#include "nsIAccessibleTypes.h"
|
||||||
#include "AccAttributes.h"
|
#include "AccAttributes.h"
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "ApplicationAccessible.h"
|
#include "ApplicationAccessible.h"
|
||||||
|
#include "nsAccessiblePivot.h"
|
||||||
#include "nsGenericHTMLElement.h"
|
#include "nsGenericHTMLElement.h"
|
||||||
#include "NotificationController.h"
|
#include "NotificationController.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
|
|
|
@ -63,6 +63,11 @@ interface nsIAccessibleDocument : nsISupports
|
||||||
*/
|
*/
|
||||||
readonly attribute unsigned long childDocumentCount;
|
readonly attribute unsigned long childDocumentCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The virtual cursor pivot this document manages.
|
||||||
|
*/
|
||||||
|
readonly attribute nsIAccessiblePivot virtualCursor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the child document accessible at the given index.
|
* Return the child document accessible at the given index.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,6 +10,7 @@ typedef short PivotMoveReason;
|
||||||
|
|
||||||
interface nsIAccessible;
|
interface nsIAccessible;
|
||||||
interface nsIAccessibleTraversalRule;
|
interface nsIAccessibleTraversalRule;
|
||||||
|
interface nsIAccessiblePivotObserver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pivot interface encapsulates a reference to a single place in an accessible
|
* The pivot interface encapsulates a reference to a single place in an accessible
|
||||||
|
@ -20,7 +21,6 @@ interface nsIAccessibleTraversalRule;
|
||||||
[scriptable, uuid(81fe5144-059b-42db-bd3a-f6ce3158d5e9)]
|
[scriptable, uuid(81fe5144-059b-42db-bd3a-f6ce3158d5e9)]
|
||||||
interface nsIAccessiblePivot : nsISupports
|
interface nsIAccessiblePivot : nsISupports
|
||||||
{
|
{
|
||||||
// XXX: These are here for the VC changed event that has yet to go away.
|
|
||||||
const PivotMoveReason REASON_NONE = 0;
|
const PivotMoveReason REASON_NONE = 0;
|
||||||
const PivotMoveReason REASON_NEXT = 1;
|
const PivotMoveReason REASON_NEXT = 1;
|
||||||
const PivotMoveReason REASON_PREV = 2;
|
const PivotMoveReason REASON_PREV = 2;
|
||||||
|
@ -28,6 +28,21 @@ interface nsIAccessiblePivot : nsISupports
|
||||||
const PivotMoveReason REASON_LAST = 4;
|
const PivotMoveReason REASON_LAST = 4;
|
||||||
const PivotMoveReason REASON_POINT = 5;
|
const PivotMoveReason REASON_POINT = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The accessible the pivot is currently pointed at.
|
||||||
|
*/
|
||||||
|
attribute nsIAccessible position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root of the subtree in which the pivot traverses.
|
||||||
|
*/
|
||||||
|
readonly attribute nsIAccessible root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The temporary modal root to which traversal is limited to.
|
||||||
|
*/
|
||||||
|
attribute nsIAccessible modalRoot;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move pivot to next object, from current position or given anchor,
|
* Move pivot to next object, from current position or given anchor,
|
||||||
* complying to given traversal rule.
|
* complying to given traversal rule.
|
||||||
|
@ -36,11 +51,14 @@ interface nsIAccessiblePivot : nsISupports
|
||||||
* @param aAnchor [in] accessible to start search from, if not provided,
|
* @param aAnchor [in] accessible to start search from, if not provided,
|
||||||
* current position will be used.
|
* current position will be used.
|
||||||
* @param aIncludeStart [in] include anchor accessible in search.
|
* @param aIncludeStart [in] include anchor accessible in search.
|
||||||
* @return next accessible node that matches rule in preorder.
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
|
* @return true on success, false if there are no new nodes to traverse to.
|
||||||
*/
|
*/
|
||||||
[optional_argc] nsIAccessible next(in nsIAccessible aAnchor,
|
[optional_argc] boolean moveNext(in nsIAccessibleTraversalRule aRule,
|
||||||
in nsIAccessibleTraversalRule aRule,
|
[optional] in nsIAccessible aAnchor,
|
||||||
[optional] in boolean aIncludeStart);
|
[optional] in boolean aIncludeStart,
|
||||||
|
[optional] in boolean aIsFromUserInput);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move pivot to previous object, from current position or given anchor,
|
* Move pivot to previous object, from current position or given anchor,
|
||||||
|
@ -50,38 +68,89 @@ interface nsIAccessiblePivot : nsISupports
|
||||||
* @param aAnchor [in] accessible to start search from, if not provided,
|
* @param aAnchor [in] accessible to start search from, if not provided,
|
||||||
* current position will be used.
|
* current position will be used.
|
||||||
* @param aIncludeStart [in] include anchor accessible in search.
|
* @param aIncludeStart [in] include anchor accessible in search.
|
||||||
* @return previous accessible node that matches rule in preorder.
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
|
* @return true on success, false if there are no new nodes to traverse to.
|
||||||
*/
|
*/
|
||||||
[optional_argc] nsIAccessible prev(in nsIAccessible aAnchor,
|
[optional_argc] boolean movePrevious(in nsIAccessibleTraversalRule aRule,
|
||||||
in nsIAccessibleTraversalRule aRule,
|
[optional] in nsIAccessible aAnchor,
|
||||||
[optional] in boolean aIncludeStart);
|
[optional] in boolean aIncludeStart,
|
||||||
|
[optional] in boolean aIsFromUserInput);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move pivot to first object in subtree complying to given traversal rule.
|
* Move pivot to first object in subtree complying to given traversal rule.
|
||||||
*
|
*
|
||||||
* @param aRule [in] traversal rule to use.
|
* @param aRule [in] traversal rule to use.
|
||||||
* @return first accessible node in subtree that matches rule in preorder.
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
|
* @return true on success, false if there are no new nodes to traverse to.
|
||||||
*/
|
*/
|
||||||
nsIAccessible first(in nsIAccessibleTraversalRule aRule);
|
[optional_argc] boolean moveFirst(in nsIAccessibleTraversalRule aRule,
|
||||||
|
[optional] in boolean aIsFromUserInput);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move pivot to last object in subtree complying to given traversal rule.
|
* Move pivot to last object in subtree complying to given traversal rule.
|
||||||
*
|
*
|
||||||
* @param aRule [in] traversal rule to use.
|
* @param aRule [in] traversal rule to use.
|
||||||
* @return last accessible node in subtree that matches rule in preorder.
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
*/
|
*/
|
||||||
nsIAccessible last(in nsIAccessibleTraversalRule aRule);
|
[optional_argc] boolean moveLast(in nsIAccessibleTraversalRule aRule,
|
||||||
|
[optional] in boolean aIsFromUserInput);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move pivot to given coordinate in screen pixels.
|
* Move pivot to given coordinate in screen pixels.
|
||||||
*
|
*
|
||||||
|
* @param aRule [in] raversal rule to use.
|
||||||
* @param aX [in] screen's x coordinate
|
* @param aX [in] screen's x coordinate
|
||||||
* @param aY [in] screen's y coordinate
|
* @param aY [in] screen's y coordinate
|
||||||
* @param aRule [in] raversal rule to use.
|
* @param aIgnoreNoMatch [in] don't unset position if no object was found
|
||||||
* @return highest accessible in subtree that matches rule at given point.
|
* at point.
|
||||||
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
|
* @return true on success, false if the pivot has not been moved.
|
||||||
*/
|
*/
|
||||||
nsIAccessible atPoint(in long aX, in long aY,
|
[optional_argc] boolean moveToPoint(in nsIAccessibleTraversalRule aRule,
|
||||||
in nsIAccessibleTraversalRule aRule);
|
in long aX, in long aY,
|
||||||
|
in boolean aIgnoreNoMatch,
|
||||||
|
[optional] in boolean aIsFromUserInput);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an observer for pivot changes.
|
||||||
|
*
|
||||||
|
* @param aObserver [in] the observer object to be notified of pivot changes.
|
||||||
|
*/
|
||||||
|
void addObserver(in nsIAccessiblePivotObserver aObserver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an observer for pivot changes.
|
||||||
|
*
|
||||||
|
* @param aObserver [in] the observer object to remove from being notified.
|
||||||
|
*/
|
||||||
|
void removeObserver(in nsIAccessiblePivotObserver aObserver);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observer interface for pivot changes.
|
||||||
|
*/
|
||||||
|
[scriptable, uuid(6006e502-3861-49bd-aba1-fa6d2e74e237)]
|
||||||
|
interface nsIAccessiblePivotObserver : nsISupports
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the pivot changes.
|
||||||
|
*
|
||||||
|
* @param aPivot [in] the pivot that has changed.
|
||||||
|
* @param aOldAccessible [in] the old pivot position before the change,
|
||||||
|
* or null.
|
||||||
|
* @param aReason [in] the reason for the pivot change.
|
||||||
|
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
||||||
|
* (default is true).
|
||||||
|
*/
|
||||||
|
void onPivotChanged(in nsIAccessiblePivot aPivot,
|
||||||
|
in nsIAccessible aOldAccessible,
|
||||||
|
in nsIAccessible aNewAccessible,
|
||||||
|
in PivotMoveReason aReason,
|
||||||
|
in boolean aIsFromUserInput);
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, uuid(e197460d-1eff-4247-b4bb-a43be1840dae)]
|
[scriptable, uuid(e197460d-1eff-4247-b4bb-a43be1840dae)]
|
||||||
|
@ -94,6 +163,29 @@ interface nsIAccessibleTraversalRule : nsISupports
|
||||||
/* Don't traverse accessibles children */
|
/* Don't traverse accessibles children */
|
||||||
const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
|
const unsigned short FILTER_IGNORE_SUBTREE = 0x2;
|
||||||
|
|
||||||
|
/* Pre-filters */
|
||||||
|
const unsigned long PREFILTER_INVISIBLE = 0x00000001;
|
||||||
|
const unsigned long PREFILTER_OFFSCREEN = 0x00000002;
|
||||||
|
const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
|
||||||
|
const unsigned long PREFILTER_TRANSPARENT = 0x00000008;
|
||||||
|
const unsigned long PREFILTER_PLATFORM_PRUNED = 0x00000010;
|
||||||
|
/**
|
||||||
|
* Pre-filter bitfield to filter out obviously ignorable nodes and lighten
|
||||||
|
* the load on match().
|
||||||
|
*/
|
||||||
|
readonly attribute unsigned long preFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of roles that the traversal rule should test for. Any node
|
||||||
|
* with a role not in this list will automatically be ignored. An empty list
|
||||||
|
* will match all roles. It should be assumed that this method is called once
|
||||||
|
* at the start of a traversal, so changing the method's return result after
|
||||||
|
* that would have no affect.
|
||||||
|
*
|
||||||
|
* @return an array of the roles to match.
|
||||||
|
*/
|
||||||
|
Array<unsigned long> getMatchRoles();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a given accessible is to be accepted in our traversal rule
|
* Determines if a given accessible is to be accepted in our traversal rule
|
||||||
*
|
*
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "mozilla/a11y/TableAccessible.h"
|
#include "mozilla/a11y/TableAccessible.h"
|
||||||
#include "mozilla/a11y/TableCellAccessible.h"
|
#include "mozilla/a11y/TableCellAccessible.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "nsIAccessiblePivot.h"
|
|
||||||
#include "XULTreeAccessible.h"
|
#include "XULTreeAccessible.h"
|
||||||
#include "Pivot.h"
|
#include "Pivot.h"
|
||||||
#include "nsAccUtils.h"
|
#include "nsAccUtils.h"
|
||||||
|
|
|
@ -40,7 +40,6 @@ BROWSER_CHROME_MANIFESTS += [
|
||||||
"tests/browser/general/browser.ini",
|
"tests/browser/general/browser.ini",
|
||||||
"tests/browser/hittest/browser.ini",
|
"tests/browser/hittest/browser.ini",
|
||||||
"tests/browser/mac/browser.ini",
|
"tests/browser/mac/browser.ini",
|
||||||
"tests/browser/pivot/browser.ini",
|
|
||||||
"tests/browser/role/browser.ini",
|
"tests/browser/role/browser.ini",
|
||||||
"tests/browser/scroll/browser.ini",
|
"tests/browser/scroll/browser.ini",
|
||||||
"tests/browser/selectable/browser.ini",
|
"tests/browser/selectable/browser.ini",
|
||||||
|
|
|
@ -46,6 +46,7 @@ skip-if = os == 'win' # Bug 1288839
|
||||||
[browser_events_show.js]
|
[browser_events_show.js]
|
||||||
[browser_events_statechange.js]
|
[browser_events_statechange.js]
|
||||||
[browser_events_textchange.js]
|
[browser_events_textchange.js]
|
||||||
|
[browser_events_vcchange.js]
|
||||||
|
|
||||||
[browser_language.js]
|
[browser_language.js]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
addAccessibleTask(
|
||||||
|
`
|
||||||
|
<p id="p1">abc</p>
|
||||||
|
<input id="input1" value="input" />`,
|
||||||
|
async function (browser) {
|
||||||
|
let onVCChanged = waitForEvent(
|
||||||
|
EVENT_VIRTUALCURSOR_CHANGED,
|
||||||
|
matchContentDoc
|
||||||
|
);
|
||||||
|
await invokeContentTask(browser, [], () => {
|
||||||
|
const { CommonUtils } = ChromeUtils.importESModule(
|
||||||
|
"chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs"
|
||||||
|
);
|
||||||
|
let vc = CommonUtils.getAccessible(
|
||||||
|
content.document,
|
||||||
|
Ci.nsIAccessibleDocument
|
||||||
|
).virtualCursor;
|
||||||
|
vc.position = CommonUtils.getAccessible(
|
||||||
|
"p1",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
content.document
|
||||||
|
);
|
||||||
|
});
|
||||||
|
let vccEvent = (await onVCChanged).QueryInterface(
|
||||||
|
nsIAccessibleVirtualCursorChangeEvent
|
||||||
|
);
|
||||||
|
is(vccEvent.newAccessible.id, "p1", "New position is correct");
|
||||||
|
ok(!vccEvent.isFromUserInput, "not user initiated");
|
||||||
|
|
||||||
|
onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc);
|
||||||
|
|
||||||
|
onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc);
|
||||||
|
await invokeContentTask(browser, [], () => {
|
||||||
|
const { CommonUtils } = ChromeUtils.importESModule(
|
||||||
|
"chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs"
|
||||||
|
);
|
||||||
|
let vc = CommonUtils.getAccessible(
|
||||||
|
content.document,
|
||||||
|
Ci.nsIAccessibleDocument
|
||||||
|
).virtualCursor;
|
||||||
|
vc.position = CommonUtils.getAccessible(
|
||||||
|
"input1",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
content.document
|
||||||
|
);
|
||||||
|
});
|
||||||
|
vccEvent = (await onVCChanged).QueryInterface(
|
||||||
|
nsIAccessibleVirtualCursorChangeEvent
|
||||||
|
);
|
||||||
|
isnot(vccEvent.oldAccessible, vccEvent.newAccessible, "positions differ");
|
||||||
|
is(vccEvent.oldAccessible.id, "p1", "Old position is correct");
|
||||||
|
is(vccEvent.newAccessible.id, "input1", "New position is correct");
|
||||||
|
ok(!vccEvent.isFromUserInput, "not user initiated");
|
||||||
|
},
|
||||||
|
{ iframe: true, remoteIframe: true }
|
||||||
|
);
|
|
@ -1,10 +0,0 @@
|
||||||
[DEFAULT]
|
|
||||||
subsuite = a11y
|
|
||||||
support-files =
|
|
||||||
!/accessible/tests/browser/shared-head.js
|
|
||||||
head.js
|
|
||||||
!/accessible/tests/mochitest/*.js
|
|
||||||
prefs =
|
|
||||||
javascript.options.asyncstack_capture_debuggee_only=false
|
|
||||||
|
|
||||||
[browser_pivot.js]
|
|
|
@ -1,103 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests Pivot API
|
|
||||||
*/
|
|
||||||
addAccessibleTask(
|
|
||||||
`
|
|
||||||
<h1 id="heading-1-1">Main Title</h1>
|
|
||||||
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
|
|
||||||
<p id="paragraph-1">
|
|
||||||
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
|
|
||||||
leo, id <a href="#">semper</a> nulla.
|
|
||||||
</p>
|
|
||||||
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
|
|
||||||
<p id="paragraph-2" aria-hidden="">
|
|
||||||
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
|
|
||||||
<p id="paragraph-3" aria-hidden="true">
|
|
||||||
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
|
|
||||||
You know, the <a href="#">singer-songwriter</a>.
|
|
||||||
</p>
|
|
||||||
<p style="opacity: 0;" id="paragraph-4">
|
|
||||||
This is completely transparent
|
|
||||||
</p>
|
|
||||||
<iframe
|
|
||||||
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
|
|
||||||
</iframe>
|
|
||||||
<div id="hide-me">Hide me</div>
|
|
||||||
<p id="links" aria-hidden="false">
|
|
||||||
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
|
|
||||||
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
|
|
||||||
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Hello<span> </span></li>
|
|
||||||
<li>World</li>
|
|
||||||
</ul>
|
|
||||||
`,
|
|
||||||
async function (browser, docAcc) {
|
|
||||||
let pivot = gAccService.createAccessiblePivot(docAcc);
|
|
||||||
testPivotSequence(pivot, HeadersTraversalRule, [
|
|
||||||
"heading-1-1",
|
|
||||||
"heading-2-2",
|
|
||||||
]);
|
|
||||||
|
|
||||||
testPivotSequence(pivot, ObjectTraversalRule, [
|
|
||||||
"Main Title",
|
|
||||||
"Lorem ipsum ",
|
|
||||||
"dolor",
|
|
||||||
" sit amet. Integer vitae urna leo, id ",
|
|
||||||
"semper",
|
|
||||||
" nulla. ",
|
|
||||||
"Second Section Title",
|
|
||||||
"Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
|
|
||||||
"An ",
|
|
||||||
"embedded",
|
|
||||||
" document.",
|
|
||||||
"Hide me",
|
|
||||||
"Link 1",
|
|
||||||
"Link 2",
|
|
||||||
"Link 3",
|
|
||||||
"Hello",
|
|
||||||
"World",
|
|
||||||
]);
|
|
||||||
|
|
||||||
let hideMeAcc = findAccessibleChildByID(docAcc, "hide-me");
|
|
||||||
let onHide = waitForEvent(EVENT_HIDE, hideMeAcc);
|
|
||||||
invokeContentTask(browser, [], () => {
|
|
||||||
content.document.getElementById("hide-me").remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
await onHide;
|
|
||||||
testFailsWithNotInTree(
|
|
||||||
() => pivot.next(hideMeAcc, ObjectTraversalRule),
|
|
||||||
"moveNext from defunct accessible should fail"
|
|
||||||
);
|
|
||||||
|
|
||||||
let linksAcc = findAccessibleChildByID(docAcc, "links");
|
|
||||||
|
|
||||||
let removedRootPivot = gAccService.createAccessiblePivot(linksAcc);
|
|
||||||
onHide = waitForEvent(EVENT_HIDE, linksAcc);
|
|
||||||
invokeContentTask(browser, [], () => {
|
|
||||||
content.document.getElementById("links").remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
await onHide;
|
|
||||||
testFailsWithNotInTree(
|
|
||||||
() => removedRootPivot.last(ObjectTraversalRule),
|
|
||||||
"moveLast with pivot with defunct root should fail"
|
|
||||||
);
|
|
||||||
|
|
||||||
let [x, y] = getBounds(findAccessibleChildByID(docAcc, "heading-1-1"));
|
|
||||||
let hitacc = pivot.atPoint(x + 1, y + 1, HeadersTraversalRule);
|
|
||||||
is(getIdOrName(hitacc), "heading-1-1", "Matching accessible at point");
|
|
||||||
|
|
||||||
hitacc = pivot.atPoint(x - 1, y - 1, HeadersTraversalRule);
|
|
||||||
ok(!hitacc, "No heading at given point");
|
|
||||||
},
|
|
||||||
{ iframe: true, remoteIframe: true, topLevel: true, chrome: true }
|
|
||||||
);
|
|
|
@ -1,122 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/* exported HeadersTraversalRule, ObjectTraversalRule, testPivotSequence, testFailsWithNotInTree */
|
|
||||||
|
|
||||||
// Load the shared-head file first.
|
|
||||||
Services.scriptloader.loadSubScript(
|
|
||||||
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
/* import-globals-from ../../mochitest/layout.js */
|
|
||||||
/* import-globals-from ../../mochitest/role.js */
|
|
||||||
/* import-globals-from ../../mochitest/states.js */
|
|
||||||
loadScripts(
|
|
||||||
{ name: "common.js", dir: MOCHITESTS_DIR },
|
|
||||||
{ name: "promisified-events.js", dir: MOCHITESTS_DIR },
|
|
||||||
{ name: "states.js", dir: MOCHITESTS_DIR },
|
|
||||||
{ name: "role.js", dir: MOCHITESTS_DIR },
|
|
||||||
{ name: "layout.js", dir: MOCHITESTS_DIR }
|
|
||||||
);
|
|
||||||
|
|
||||||
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
|
|
||||||
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
|
|
||||||
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
|
||||||
|
|
||||||
const NS_ERROR_NOT_IN_TREE = 0x80780026;
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Traversal rules
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rule object to traverse all focusable nodes and text nodes.
|
|
||||||
*/
|
|
||||||
const HeadersTraversalRule = {
|
|
||||||
match(acc) {
|
|
||||||
return acc.role == ROLE_HEADING ? FILTER_MATCH : FILTER_IGNORE;
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Traversal rule for all focusable nodes or leafs.
|
|
||||||
*/
|
|
||||||
const ObjectTraversalRule = {
|
|
||||||
match(acc) {
|
|
||||||
let [state, extstate] = getStates(acc);
|
|
||||||
if (state & STATE_INVISIBLE) {
|
|
||||||
return FILTER_IGNORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((extstate & EXT_STATE_OPAQUE) == 0) {
|
|
||||||
return FILTER_IGNORE | FILTER_IGNORE_SUBTREE;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rv = FILTER_IGNORE;
|
|
||||||
let role = acc.role;
|
|
||||||
if (
|
|
||||||
hasState(acc, STATE_FOCUSABLE) &&
|
|
||||||
role != ROLE_DOCUMENT &&
|
|
||||||
role != ROLE_INTERNAL_FRAME
|
|
||||||
) {
|
|
||||||
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
|
|
||||||
} else if (
|
|
||||||
acc.childCount == 0 &&
|
|
||||||
role != ROLE_LISTITEM_MARKER &&
|
|
||||||
acc.name.trim()
|
|
||||||
) {
|
|
||||||
rv = FILTER_MATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
|
||||||
};
|
|
||||||
|
|
||||||
function getIdOrName(acc) {
|
|
||||||
let id = getAccessibleDOMNodeID(acc);
|
|
||||||
if (id) {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
return acc.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
function* pivotNextGenerator(pivot, rule) {
|
|
||||||
for (let acc = pivot.first(rule); acc; acc = pivot.next(acc, rule)) {
|
|
||||||
yield acc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function* pivotPreviousGenerator(pivot, rule) {
|
|
||||||
for (let acc = pivot.last(rule); acc; acc = pivot.prev(acc, rule)) {
|
|
||||||
yield acc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPivotSequence(pivot, rule, expectedSequence) {
|
|
||||||
is(
|
|
||||||
JSON.stringify([...pivotNextGenerator(pivot, rule)].map(getIdOrName)),
|
|
||||||
JSON.stringify(expectedSequence),
|
|
||||||
"Forward pivot sequence is correct"
|
|
||||||
);
|
|
||||||
is(
|
|
||||||
JSON.stringify([...pivotPreviousGenerator(pivot, rule)].map(getIdOrName)),
|
|
||||||
JSON.stringify([...expectedSequence].reverse()),
|
|
||||||
"Reverse pivot sequence is correct"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testFailsWithNotInTree(func, msg) {
|
|
||||||
try {
|
|
||||||
func();
|
|
||||||
ok(false, msg);
|
|
||||||
} catch (x) {
|
|
||||||
is(x.result, NS_ERROR_NOT_IN_TREE, `Expecting NOT_IN_TREE: ${msg}`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,6 +18,7 @@ A11Y_MANIFESTS += [
|
||||||
"hyperlink/a11y.ini",
|
"hyperlink/a11y.ini",
|
||||||
"hypertext/a11y.ini",
|
"hypertext/a11y.ini",
|
||||||
"name/a11y.ini",
|
"name/a11y.ini",
|
||||||
|
"pivot/a11y.ini",
|
||||||
"relations/a11y.ini",
|
"relations/a11y.ini",
|
||||||
"role/a11y.ini",
|
"role/a11y.ini",
|
||||||
"scroll/a11y.ini",
|
"scroll/a11y.ini",
|
||||||
|
|
|
@ -0,0 +1,575 @@
|
||||||
|
/* import-globals-from common.js */
|
||||||
|
/* import-globals-from events.js */
|
||||||
|
/* import-globals-from role.js */
|
||||||
|
/* import-globals-from states.js */
|
||||||
|
/* import-globals-from text.js */
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
|
||||||
|
const PREFILTER_TRANSPARENT = nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
|
||||||
|
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||||
|
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||||
|
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||||
|
|
||||||
|
const NS_ERROR_NOT_IN_TREE = 0x80780026;
|
||||||
|
const NS_ERROR_INVALID_ARG = 0x80070057;
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Traversal rules
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rule object to traverse all focusable nodes and text nodes.
|
||||||
|
*/
|
||||||
|
var HeadersTraversalRule = {
|
||||||
|
getMatchRoles() {
|
||||||
|
return [ROLE_HEADING];
|
||||||
|
},
|
||||||
|
|
||||||
|
preFilter: PREFILTER_INVISIBLE,
|
||||||
|
|
||||||
|
match(aAccessible) {
|
||||||
|
return FILTER_MATCH;
|
||||||
|
},
|
||||||
|
|
||||||
|
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traversal rule for all focusable nodes or leafs.
|
||||||
|
*/
|
||||||
|
var ObjectTraversalRule = {
|
||||||
|
getMatchRoles() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
preFilter: PREFILTER_INVISIBLE | PREFILTER_TRANSPARENT,
|
||||||
|
|
||||||
|
match(aAccessible) {
|
||||||
|
var rv = FILTER_IGNORE;
|
||||||
|
var role = aAccessible.role;
|
||||||
|
if (
|
||||||
|
hasState(aAccessible, STATE_FOCUSABLE) &&
|
||||||
|
role != ROLE_DOCUMENT &&
|
||||||
|
role != ROLE_INTERNAL_FRAME
|
||||||
|
) {
|
||||||
|
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
|
||||||
|
} else if (
|
||||||
|
aAccessible.childCount == 0 &&
|
||||||
|
role != ROLE_LISTITEM_MARKER &&
|
||||||
|
aAccessible.name.trim()
|
||||||
|
) {
|
||||||
|
rv = FILTER_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
},
|
||||||
|
|
||||||
|
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
||||||
|
};
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Virtual state invokers and checkers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checker for virtual cursor changed events.
|
||||||
|
*/
|
||||||
|
function VCChangedChecker(
|
||||||
|
aDocAcc,
|
||||||
|
aIdOrNameOrAcc,
|
||||||
|
aTextOffsets,
|
||||||
|
aPivotMoveMethod,
|
||||||
|
aIsFromUserInput
|
||||||
|
) {
|
||||||
|
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
|
||||||
|
|
||||||
|
this.match = function VCChangedChecker_match(aEvent) {
|
||||||
|
var event = null;
|
||||||
|
try {
|
||||||
|
event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedReason =
|
||||||
|
VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
|
||||||
|
nsIAccessiblePivot.REASON_NONE;
|
||||||
|
|
||||||
|
return event.reason == expectedReason;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.check = function VCChangedChecker_check(aEvent) {
|
||||||
|
SimpleTest.info("VCChangedChecker_check");
|
||||||
|
|
||||||
|
var event = null;
|
||||||
|
try {
|
||||||
|
event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
|
||||||
|
} catch (e) {
|
||||||
|
SimpleTest.ok(false, "Does not support correct interface: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = aDocAcc.virtualCursor.position;
|
||||||
|
var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
|
||||||
|
var nameMatches = position && position.name == aIdOrNameOrAcc;
|
||||||
|
var accMatches = position == aIdOrNameOrAcc;
|
||||||
|
|
||||||
|
SimpleTest.ok(
|
||||||
|
idMatches || nameMatches || accMatches,
|
||||||
|
"id or name matches - expecting " +
|
||||||
|
prettyName(aIdOrNameOrAcc) +
|
||||||
|
", got '" +
|
||||||
|
prettyName(position)
|
||||||
|
);
|
||||||
|
|
||||||
|
SimpleTest.is(
|
||||||
|
aEvent.isFromUserInput,
|
||||||
|
aIsFromUserInput,
|
||||||
|
"Expected user input is " + aIsFromUserInput + "\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
SimpleTest.is(
|
||||||
|
event.newAccessible,
|
||||||
|
position,
|
||||||
|
"new position in event is incorrect"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (aTextOffsets) {
|
||||||
|
SimpleTest.is(
|
||||||
|
aDocAcc.virtualCursor.startOffset,
|
||||||
|
aTextOffsets[0],
|
||||||
|
"wrong start offset"
|
||||||
|
);
|
||||||
|
SimpleTest.is(
|
||||||
|
aDocAcc.virtualCursor.endOffset,
|
||||||
|
aTextOffsets[1],
|
||||||
|
"wrong end offset"
|
||||||
|
);
|
||||||
|
SimpleTest.is(
|
||||||
|
event.newStartOffset,
|
||||||
|
aTextOffsets[0],
|
||||||
|
"wrong start offset in event"
|
||||||
|
);
|
||||||
|
SimpleTest.is(
|
||||||
|
event.newEndOffset,
|
||||||
|
aTextOffsets[1],
|
||||||
|
"wrong end offset in event"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevPosAndOffset = VCChangedChecker.getPreviousPosAndOffset(
|
||||||
|
aDocAcc.virtualCursor
|
||||||
|
);
|
||||||
|
|
||||||
|
if (prevPosAndOffset) {
|
||||||
|
SimpleTest.is(
|
||||||
|
event.oldAccessible,
|
||||||
|
prevPosAndOffset.position,
|
||||||
|
"previous position does not match"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VCChangedChecker.prevPosAndOffset = {};
|
||||||
|
|
||||||
|
VCChangedChecker.storePreviousPosAndOffset = function storePreviousPosAndOffset(
|
||||||
|
aPivot
|
||||||
|
) {
|
||||||
|
VCChangedChecker.prevPosAndOffset[aPivot] = {
|
||||||
|
position: aPivot.position,
|
||||||
|
startOffset: aPivot.startOffset,
|
||||||
|
endOffset: aPivot.endOffset,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
VCChangedChecker.getPreviousPosAndOffset = function getPreviousPosAndOffset(
|
||||||
|
aPivot
|
||||||
|
) {
|
||||||
|
return VCChangedChecker.prevPosAndOffset[aPivot];
|
||||||
|
};
|
||||||
|
|
||||||
|
VCChangedChecker.methodReasonMap = {
|
||||||
|
moveNext: nsIAccessiblePivot.REASON_NEXT,
|
||||||
|
movePrevious: nsIAccessiblePivot.REASON_PREV,
|
||||||
|
moveFirst: nsIAccessiblePivot.REASON_FIRST,
|
||||||
|
moveLast: nsIAccessiblePivot.REASON_LAST,
|
||||||
|
setTextRange: nsIAccessiblePivot.REASON_NONE,
|
||||||
|
moveNextByText: nsIAccessiblePivot.REASON_NEXT,
|
||||||
|
movePreviousByText: nsIAccessiblePivot.REASON_PREV,
|
||||||
|
moveToPoint: nsIAccessiblePivot.REASON_POINT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a text range in the pivot and wait for virtual cursor change event.
|
||||||
|
*
|
||||||
|
* @param aDocAcc [in] document that manages the virtual cursor
|
||||||
|
* @param aTextAccessible [in] accessible to set to virtual cursor's position
|
||||||
|
* @param aTextOffsets [in] start and end offsets of text range to set in
|
||||||
|
* virtual cursor.
|
||||||
|
*/
|
||||||
|
function setVCRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets) {
|
||||||
|
this.invoke = function virtualCursorChangedInvoker_invoke() {
|
||||||
|
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
|
||||||
|
SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
|
||||||
|
aDocAcc.virtualCursor.setTextRange(
|
||||||
|
aTextAccessible,
|
||||||
|
aTextOffsets[0],
|
||||||
|
aTextOffsets[1]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function setVCRangeInvoker_getID() {
|
||||||
|
return (
|
||||||
|
"Set offset in " +
|
||||||
|
prettyName(aTextAccessible) +
|
||||||
|
" to (" +
|
||||||
|
aTextOffsets[0] +
|
||||||
|
", " +
|
||||||
|
aTextOffsets[1] +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.eventSeq = [
|
||||||
|
new VCChangedChecker(
|
||||||
|
aDocAcc,
|
||||||
|
aTextAccessible,
|
||||||
|
aTextOffsets,
|
||||||
|
"setTextRange",
|
||||||
|
true
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the pivot and wait for virtual cursor change event.
|
||||||
|
*
|
||||||
|
* @param aDocAcc [in] document that manages the virtual cursor
|
||||||
|
* @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
|
||||||
|
* @param aRule [in] traversal rule object
|
||||||
|
* @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
|
||||||
|
* virtual cursor to land on after performing move method.
|
||||||
|
* false if no move is expected.
|
||||||
|
* @param aIsFromUserInput [in] set user input flag when invoking method, and
|
||||||
|
* expect it in the event.
|
||||||
|
*/
|
||||||
|
function setVCPosInvoker(
|
||||||
|
aDocAcc,
|
||||||
|
aPivotMoveMethod,
|
||||||
|
aRule,
|
||||||
|
aIdOrNameOrAcc,
|
||||||
|
aIsFromUserInput
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line mozilla/no-compare-against-boolean-literals
|
||||||
|
var expectMove = aIdOrNameOrAcc != false;
|
||||||
|
this.invoke = function virtualCursorChangedInvoker_invoke() {
|
||||||
|
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
|
||||||
|
if (aPivotMoveMethod && aRule) {
|
||||||
|
var moved = false;
|
||||||
|
switch (aPivotMoveMethod) {
|
||||||
|
case "moveFirst":
|
||||||
|
case "moveLast":
|
||||||
|
moved = aDocAcc.virtualCursor[aPivotMoveMethod](
|
||||||
|
aRule,
|
||||||
|
aIsFromUserInput === undefined ? true : aIsFromUserInput
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "moveNext":
|
||||||
|
case "movePrevious":
|
||||||
|
moved = aDocAcc.virtualCursor[aPivotMoveMethod](
|
||||||
|
aRule,
|
||||||
|
aDocAcc.virtualCursor.position,
|
||||||
|
false,
|
||||||
|
aIsFromUserInput === undefined ? true : aIsFromUserInput
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SimpleTest.is(
|
||||||
|
!!moved,
|
||||||
|
!!expectMove,
|
||||||
|
"moved pivot with " + aPivotMoveMethod + " to " + aIdOrNameOrAcc
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function setVCPosInvoker_getID() {
|
||||||
|
return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (expectMove) {
|
||||||
|
this.eventSeq = [
|
||||||
|
new VCChangedChecker(
|
||||||
|
aDocAcc,
|
||||||
|
aIdOrNameOrAcc,
|
||||||
|
null,
|
||||||
|
aPivotMoveMethod,
|
||||||
|
aIsFromUserInput === undefined ? !!aPivotMoveMethod : aIsFromUserInput
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
this.eventSeq = [];
|
||||||
|
this.unexpectedEventSeq = [
|
||||||
|
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the pivot to the position under the point.
|
||||||
|
*
|
||||||
|
* @param aDocAcc [in] document that manages the virtual cursor
|
||||||
|
* @param aX [in] screen x coordinate
|
||||||
|
* @param aY [in] screen y coordinate
|
||||||
|
* @param aIgnoreNoMatch [in] don't unset position if no object was found at
|
||||||
|
* point.
|
||||||
|
* @param aRule [in] traversal rule object
|
||||||
|
* @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
|
||||||
|
* virtual cursor to land on after performing move method.
|
||||||
|
* false if no move is expected.
|
||||||
|
*/
|
||||||
|
function moveVCCoordInvoker(
|
||||||
|
aDocAcc,
|
||||||
|
aX,
|
||||||
|
aY,
|
||||||
|
aIgnoreNoMatch,
|
||||||
|
aRule,
|
||||||
|
aIdOrNameOrAcc
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line mozilla/no-compare-against-boolean-literals
|
||||||
|
var expectMove = aIdOrNameOrAcc != false;
|
||||||
|
this.invoke = function virtualCursorChangedInvoker_invoke() {
|
||||||
|
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
|
||||||
|
var moved = aDocAcc.virtualCursor.moveToPoint(
|
||||||
|
aRule,
|
||||||
|
aX,
|
||||||
|
aY,
|
||||||
|
aIgnoreNoMatch
|
||||||
|
);
|
||||||
|
SimpleTest.ok(
|
||||||
|
(expectMove && moved) || (!expectMove && !moved),
|
||||||
|
"moved pivot"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function setVCPosInvoker_getID() {
|
||||||
|
return (
|
||||||
|
"Do " + (expectMove ? "" : "no-op ") + "moveToPoint " + aIdOrNameOrAcc
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (expectMove) {
|
||||||
|
this.eventSeq = [
|
||||||
|
new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, "moveToPoint", true),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
this.eventSeq = [];
|
||||||
|
this.unexpectedEventSeq = [
|
||||||
|
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the pivot modalRoot
|
||||||
|
*
|
||||||
|
* @param aDocAcc [in] document that manages the virtual cursor
|
||||||
|
* @param aModalRootAcc [in] accessible of the modal root, or null
|
||||||
|
* @param aExpectedResult [in] error result expected. 0 if expecting success
|
||||||
|
*/
|
||||||
|
function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult) {
|
||||||
|
this.invoke = function setModalRootInvoker_invoke() {
|
||||||
|
var errorResult = 0;
|
||||||
|
try {
|
||||||
|
aDocAcc.virtualCursor.modalRoot = aModalRootAcc;
|
||||||
|
} catch (x) {
|
||||||
|
SimpleTest.ok(
|
||||||
|
x.result,
|
||||||
|
"Unexpected exception when changing modal root: " + x
|
||||||
|
);
|
||||||
|
errorResult = x.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.is(
|
||||||
|
errorResult,
|
||||||
|
aExpectedResult,
|
||||||
|
"Did not get expected result when changing modalRoot"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function setModalRootInvoker_getID() {
|
||||||
|
return "Set modalRoot to " + prettyName(aModalRootAcc);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.eventSeq = [];
|
||||||
|
this.unexpectedEventSeq = [
|
||||||
|
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add invokers to a queue to test a rule and an expected sequence of element ids
|
||||||
|
* or accessible names for that rule in the given document.
|
||||||
|
*
|
||||||
|
* @param aQueue [in] event queue in which to push invoker sequence.
|
||||||
|
* @param aDocAcc [in] the managing document of the virtual cursor we are
|
||||||
|
* testing
|
||||||
|
* @param aRule [in] the traversal rule to use in the invokers
|
||||||
|
* @param aModalRoot [in] a modal root to use in this traversal sequence
|
||||||
|
* @param aSequence [in] a sequence of accessible names or element ids to expect
|
||||||
|
* with the given rule in the given document
|
||||||
|
*/
|
||||||
|
function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence) {
|
||||||
|
aDocAcc.virtualCursor.position = null;
|
||||||
|
|
||||||
|
// Add modal root (if any)
|
||||||
|
aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
|
||||||
|
|
||||||
|
aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
|
||||||
|
|
||||||
|
for (let i = 1; i < aSequence.length; i++) {
|
||||||
|
let invoker = new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
|
||||||
|
aQueue.push(invoker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No further more matches for given rule, expect no virtual cursor changes.
|
||||||
|
aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
|
||||||
|
|
||||||
|
for (let i = aSequence.length - 2; i >= 0; i--) {
|
||||||
|
let invoker = new setVCPosInvoker(
|
||||||
|
aDocAcc,
|
||||||
|
"movePrevious",
|
||||||
|
aRule,
|
||||||
|
aSequence[i]
|
||||||
|
);
|
||||||
|
aQueue.push(invoker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No previous more matches for given rule, expect no virtual cursor changes.
|
||||||
|
aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
|
||||||
|
|
||||||
|
aQueue.push(
|
||||||
|
new setVCPosInvoker(
|
||||||
|
aDocAcc,
|
||||||
|
"moveLast",
|
||||||
|
aRule,
|
||||||
|
aSequence[aSequence.length - 1]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// No further more matches for given rule, expect no virtual cursor changes.
|
||||||
|
aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
|
||||||
|
|
||||||
|
// set isFromUserInput to false, just to test..
|
||||||
|
aQueue.push(
|
||||||
|
new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0], false)
|
||||||
|
);
|
||||||
|
|
||||||
|
// No previous more matches for given rule, expect no virtual cursor changes.
|
||||||
|
aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
|
||||||
|
|
||||||
|
// Remove modal root (if any).
|
||||||
|
aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checker for removing an accessible while the virtual cursor is on it.
|
||||||
|
*/
|
||||||
|
function removeVCPositionChecker(aDocAcc, aHiddenParentAcc) {
|
||||||
|
this.__proto__ = new invokerChecker(EVENT_REORDER, aHiddenParentAcc);
|
||||||
|
|
||||||
|
this.check = function removeVCPositionChecker_check(aEvent) {
|
||||||
|
var errorResult = 0;
|
||||||
|
try {
|
||||||
|
aDocAcc.virtualCursor.moveNext(ObjectTraversalRule);
|
||||||
|
} catch (x) {
|
||||||
|
errorResult = x.result;
|
||||||
|
}
|
||||||
|
SimpleTest.is(
|
||||||
|
errorResult,
|
||||||
|
NS_ERROR_NOT_IN_TREE,
|
||||||
|
"Expecting NOT_IN_TREE error when moving pivot from invalid position."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put the virtual cursor's position on an object, and then remove it.
|
||||||
|
*
|
||||||
|
* @param aDocAcc [in] document that manages the virtual cursor
|
||||||
|
* @param aPosNode [in] DOM node to hide after virtual cursor's position is
|
||||||
|
* set to it.
|
||||||
|
*/
|
||||||
|
function removeVCPositionInvoker(aDocAcc, aPosNode) {
|
||||||
|
this.accessible = getAccessible(aPosNode);
|
||||||
|
this.invoke = function removeVCPositionInvoker_invoke() {
|
||||||
|
aDocAcc.virtualCursor.position = this.accessible;
|
||||||
|
aPosNode.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function removeVCPositionInvoker_getID() {
|
||||||
|
return "Bring virtual cursor to accessible, and remove its DOM node.";
|
||||||
|
};
|
||||||
|
|
||||||
|
this.eventSeq = [
|
||||||
|
new removeVCPositionChecker(aDocAcc, this.accessible.parent),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checker for removing the pivot root and then calling moveFirst, and
|
||||||
|
* checking that an exception is thrown.
|
||||||
|
*/
|
||||||
|
function removeVCRootChecker(aPivot) {
|
||||||
|
this.__proto__ = new invokerChecker(EVENT_REORDER, aPivot.root.parent);
|
||||||
|
|
||||||
|
this.check = function removeVCRootChecker_check(aEvent) {
|
||||||
|
var errorResult = 0;
|
||||||
|
try {
|
||||||
|
aPivot.moveLast(ObjectTraversalRule);
|
||||||
|
} catch (x) {
|
||||||
|
errorResult = x.result;
|
||||||
|
}
|
||||||
|
SimpleTest.is(
|
||||||
|
errorResult,
|
||||||
|
NS_ERROR_NOT_IN_TREE,
|
||||||
|
"Expecting NOT_IN_TREE error when moving pivot from invalid position."
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pivot, remove its root, and perform an operation where the root is
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
|
* @param aRootNode [in] DOM node of which accessible will be the root of the
|
||||||
|
* pivot. Should have more than one child.
|
||||||
|
*/
|
||||||
|
function removeVCRootInvoker(aRootNode) {
|
||||||
|
this.pivot = gAccService.createAccessiblePivot(getAccessible(aRootNode));
|
||||||
|
this.invoke = function removeVCRootInvoker_invoke() {
|
||||||
|
this.pivot.position = this.pivot.root.firstChild;
|
||||||
|
aRootNode.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getID = function removeVCRootInvoker_getID() {
|
||||||
|
return "Remove root of pivot from tree.";
|
||||||
|
};
|
||||||
|
|
||||||
|
this.eventSeq = [new removeVCRootChecker(this.pivot)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A debug utility for writing proper sequences for queueTraversalSequence.
|
||||||
|
*/
|
||||||
|
function dumpTraversalSequence(aPivot, aRule) {
|
||||||
|
var sequence = [];
|
||||||
|
if (aPivot.moveFirst(aRule)) {
|
||||||
|
do {
|
||||||
|
sequence.push("'" + prettyName(aPivot.position) + "'");
|
||||||
|
} while (aPivot.moveNext(aRule));
|
||||||
|
}
|
||||||
|
SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
[DEFAULT]
|
||||||
|
support-files =
|
||||||
|
doc_virtualcursor.html
|
||||||
|
!/accessible/tests/mochitest/*.js
|
||||||
|
|
||||||
|
[test_virtualcursor.html]
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Pivot test document</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="heading-1-1">Main Title</h1>
|
||||||
|
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
|
||||||
|
<p id="paragraph-1">
|
||||||
|
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
|
||||||
|
leo, id <a href="#">semper</a> nulla.
|
||||||
|
</p>
|
||||||
|
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
|
||||||
|
<p id="paragraph-2" aria-hidden="">
|
||||||
|
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
|
||||||
|
<p id="paragraph-3" aria-hidden="true">
|
||||||
|
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
|
||||||
|
You know, the <a href="#">singer-songwriter</a>.
|
||||||
|
</p>
|
||||||
|
<p style="opacity: 0;" id="paragraph-4">
|
||||||
|
This is completely transparent
|
||||||
|
</p>
|
||||||
|
<iframe
|
||||||
|
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
|
||||||
|
</iframe>
|
||||||
|
<div id="hide-me">Hide me</div>
|
||||||
|
<p id="links" aria-hidden="false">
|
||||||
|
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
|
||||||
|
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
|
||||||
|
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Hello<span> </span></li>
|
||||||
|
<li>World</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,112 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Tests pivot functionality in virtual cursors</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||||
|
|
||||||
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
|
||||||
|
</script>
|
||||||
|
<script src="chrome://mochikit/content/chrome-harness.js">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="../common.js"></script>
|
||||||
|
<script type="application/javascript" src="../browser.js"></script>
|
||||||
|
<script type="application/javascript" src="../events.js"></script>
|
||||||
|
<script type="application/javascript" src="../role.js"></script>
|
||||||
|
<script type="application/javascript" src="../states.js"></script>
|
||||||
|
<script type="application/javascript" src="../pivot.js"></script>
|
||||||
|
<script type="application/javascript" src="../layout.js"></script>
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
var gBrowserWnd = null;
|
||||||
|
var gQueue = null;
|
||||||
|
|
||||||
|
function doTest() {
|
||||||
|
var rootAcc = getAccessible(browserDocument(), [nsIAccessibleDocument]);
|
||||||
|
ok(rootAcc.virtualCursor,
|
||||||
|
"root document does not have virtualCursor");
|
||||||
|
|
||||||
|
var doc = currentTabDocument();
|
||||||
|
var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
|
||||||
|
|
||||||
|
// Test that embedded documents have their own virtual cursor.
|
||||||
|
is(docAcc.childDocumentCount, 1, "Expecting one child document");
|
||||||
|
ok(docAcc.getChildDocumentAt(0).virtualCursor,
|
||||||
|
"child document does not have virtualCursor");
|
||||||
|
|
||||||
|
gQueue = new eventQueue();
|
||||||
|
|
||||||
|
gQueue.onFinish = function onFinish() {
|
||||||
|
closeBrowserWindow();
|
||||||
|
};
|
||||||
|
|
||||||
|
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
|
||||||
|
["heading-1-1", "heading-2-2"]);
|
||||||
|
|
||||||
|
queueTraversalSequence(
|
||||||
|
gQueue, docAcc, ObjectTraversalRule, null,
|
||||||
|
["Main Title", "Lorem ipsum ",
|
||||||
|
"dolor", " sit amet. Integer vitae urna leo, id ",
|
||||||
|
"semper", " nulla. ", "Second Section Title",
|
||||||
|
"Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
|
||||||
|
"An ", "embedded", " document.", "Hide me", "Link 1", "Link 2",
|
||||||
|
"Link 3", "Hello", "World"]);
|
||||||
|
|
||||||
|
gQueue.push(new removeVCPositionInvoker(
|
||||||
|
docAcc, doc.getElementById("hide-me")));
|
||||||
|
|
||||||
|
gQueue.push(new removeVCRootInvoker(
|
||||||
|
doc.getElementById("links")));
|
||||||
|
|
||||||
|
var [x, y] = getBounds(getAccessible(doc.getElementById("heading-1-1")));
|
||||||
|
gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
|
||||||
|
HeadersTraversalRule, "heading-1-1"));
|
||||||
|
|
||||||
|
// Already on the point, so we should not get a move event.
|
||||||
|
gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
|
||||||
|
HeadersTraversalRule, false));
|
||||||
|
|
||||||
|
// Attempting a coordinate outside any header, should not move.
|
||||||
|
gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, true,
|
||||||
|
HeadersTraversalRule, false));
|
||||||
|
|
||||||
|
// Attempting a coordinate outside any header, should move to null
|
||||||
|
gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false,
|
||||||
|
HeadersTraversalRule, null));
|
||||||
|
|
||||||
|
queueTraversalSequence(
|
||||||
|
gQueue, docAcc, ObjectTraversalRule,
|
||||||
|
getAccessible(doc.getElementById("paragraph-1")),
|
||||||
|
["Lorem ipsum ", "dolor", " sit amet. Integer vitae urna leo, id ",
|
||||||
|
"semper", " nulla. "]);
|
||||||
|
|
||||||
|
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
|
||||||
|
NS_ERROR_INVALID_ARG));
|
||||||
|
|
||||||
|
gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "dolor"));
|
||||||
|
|
||||||
|
gQueue.invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
addLoadEvent(function() {
|
||||||
|
/* We open a new browser because we need to test with a top-level content
|
||||||
|
document. */
|
||||||
|
openBrowserWindow(
|
||||||
|
doTest,
|
||||||
|
getRootDirectory(window.location.href) + "doc_virtualcursor.html");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body id="body">
|
||||||
|
|
||||||
|
<a target="_blank"
|
||||||
|
title="Introduce virtual cursor/soft focus functionality to a11y API"
|
||||||
|
href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none"></div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -14,7 +14,6 @@ UNIFIED_SOURCES += [
|
||||||
"xpcAccessibleHyperLink.cpp",
|
"xpcAccessibleHyperLink.cpp",
|
||||||
"xpcAccessibleHyperText.cpp",
|
"xpcAccessibleHyperText.cpp",
|
||||||
"xpcAccessibleImage.cpp",
|
"xpcAccessibleImage.cpp",
|
||||||
"xpcAccessiblePivot.cpp",
|
|
||||||
"xpcAccessibleSelectable.cpp",
|
"xpcAccessibleSelectable.cpp",
|
||||||
"xpcAccessibleTable.cpp",
|
"xpcAccessibleTable.cpp",
|
||||||
"xpcAccessibleTableCell.cpp",
|
"xpcAccessibleTableCell.cpp",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
|
|
||||||
#include "xpcAccessiblePivot.h"
|
#include "nsAccessiblePivot.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "xpcAccessibleApplication.h"
|
#include "xpcAccessibleApplication.h"
|
||||||
|
@ -236,7 +236,10 @@ xpcAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
|
||||||
NS_ENSURE_ARG(aRoot);
|
NS_ENSURE_ARG(aRoot);
|
||||||
*aPivot = nullptr;
|
*aPivot = nullptr;
|
||||||
|
|
||||||
xpcAccessiblePivot* pivot = new xpcAccessiblePivot(aRoot);
|
LocalAccessible* accessibleRoot = aRoot->ToInternalAccessible();
|
||||||
|
NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
|
nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
|
||||||
NS_ADDREF(*aPivot = pivot);
|
NS_ADDREF(*aPivot = pivot);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -132,6 +132,17 @@ xpcAccessibleDocument::GetChildDocumentAt(uint32_t aIndex,
|
||||||
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
|
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
xpcAccessibleDocument::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aVirtualCursor);
|
||||||
|
*aVirtualCursor = nullptr;
|
||||||
|
|
||||||
|
if (!Intl()) return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
NS_ADDREF(*aVirtualCursor = Intl()->VirtualCursor());
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// xpcAccessibleDocument
|
// xpcAccessibleDocument
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
|
||||||
NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) final;
|
NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) final;
|
||||||
NS_IMETHOD GetChildDocumentAt(uint32_t aIndex,
|
NS_IMETHOD GetChildDocumentAt(uint32_t aIndex,
|
||||||
nsIAccessibleDocument** aDocument) final;
|
nsIAccessibleDocument** aDocument) final;
|
||||||
|
NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) final;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return XPCOM wrapper for the internal accessible.
|
* Return XPCOM wrapper for the internal accessible.
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim: set ts=2 et sw=2 tw=80: */
|
|
||||||
/* 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 "xpcAccessiblePivot.h"
|
|
||||||
|
|
||||||
#include "Pivot.h"
|
|
||||||
|
|
||||||
using namespace mozilla::a11y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object that stores a given traversal rule during the pivot movement.
|
|
||||||
*/
|
|
||||||
class xpcPivotRule : public PivotRule {
|
|
||||||
public:
|
|
||||||
explicit xpcPivotRule(nsIAccessibleTraversalRule* aRule) : mRule(aRule) {}
|
|
||||||
~xpcPivotRule() {}
|
|
||||||
|
|
||||||
virtual uint16_t Match(Accessible* aAcc) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// xpcAccessiblePivot
|
|
||||||
|
|
||||||
xpcAccessiblePivot::xpcAccessiblePivot(nsIAccessible* aRoot) : mRoot(aRoot) {
|
|
||||||
NS_ASSERTION(aRoot, "A root accessible is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
xpcAccessiblePivot::~xpcAccessiblePivot() {}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// nsISupports
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION(xpcAccessiblePivot, mRoot)
|
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessiblePivot)
|
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
|
|
||||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
|
|
||||||
NS_INTERFACE_MAP_END
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(xpcAccessiblePivot)
|
|
||||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessiblePivot)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// nsIAccessiblePivot
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
xpcAccessiblePivot::Next(nsIAccessible* aAnchor,
|
|
||||||
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
|
|
||||||
uint8_t aArgc, nsIAccessible** aResult) {
|
|
||||||
NS_ENSURE_ARG(aResult);
|
|
||||||
NS_ENSURE_ARG(aRule);
|
|
||||||
|
|
||||||
Accessible* root = Root();
|
|
||||||
Accessible* anchor = aAnchor->ToInternalGeneric();
|
|
||||||
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
|
|
||||||
|
|
||||||
Pivot pivot(Root());
|
|
||||||
xpcPivotRule rule(aRule);
|
|
||||||
Accessible* result =
|
|
||||||
pivot.Next(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
|
|
||||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
xpcAccessiblePivot::Prev(nsIAccessible* aAnchor,
|
|
||||||
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
|
|
||||||
uint8_t aArgc, nsIAccessible** aResult) {
|
|
||||||
NS_ENSURE_ARG(aResult);
|
|
||||||
NS_ENSURE_ARG(aRule);
|
|
||||||
|
|
||||||
Accessible* root = Root();
|
|
||||||
Accessible* anchor = aAnchor->ToInternalGeneric();
|
|
||||||
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
|
|
||||||
|
|
||||||
Pivot pivot(Root());
|
|
||||||
xpcPivotRule rule(aRule);
|
|
||||||
Accessible* result =
|
|
||||||
pivot.Prev(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
|
|
||||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
xpcAccessiblePivot::First(nsIAccessibleTraversalRule* aRule,
|
|
||||||
nsIAccessible** aResult) {
|
|
||||||
NS_ENSURE_ARG(aResult);
|
|
||||||
NS_ENSURE_ARG(aRule);
|
|
||||||
|
|
||||||
Accessible* root = Root();
|
|
||||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
|
||||||
|
|
||||||
Pivot pivot(root);
|
|
||||||
xpcPivotRule rule(aRule);
|
|
||||||
Accessible* result = pivot.First(rule);
|
|
||||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
xpcAccessiblePivot::Last(nsIAccessibleTraversalRule* aRule,
|
|
||||||
nsIAccessible** aResult) {
|
|
||||||
NS_ENSURE_ARG(aResult);
|
|
||||||
NS_ENSURE_ARG(aRule);
|
|
||||||
|
|
||||||
Accessible* root = Root();
|
|
||||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
|
||||||
|
|
||||||
Pivot pivot(root);
|
|
||||||
xpcPivotRule rule(aRule);
|
|
||||||
Accessible* result = pivot.Last(rule);
|
|
||||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
xpcAccessiblePivot::AtPoint(int32_t aX, int32_t aY,
|
|
||||||
nsIAccessibleTraversalRule* aRule,
|
|
||||||
nsIAccessible** aResult) {
|
|
||||||
NS_ENSURE_ARG_POINTER(aResult);
|
|
||||||
NS_ENSURE_ARG_POINTER(aRule);
|
|
||||||
|
|
||||||
Accessible* root = Root();
|
|
||||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
|
||||||
|
|
||||||
xpcPivotRule rule(aRule);
|
|
||||||
Pivot pivot(root);
|
|
||||||
|
|
||||||
Accessible* result = pivot.AtPoint(aX, aY, rule);
|
|
||||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t xpcPivotRule::Match(Accessible* aAcc) {
|
|
||||||
uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
|
||||||
|
|
||||||
DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult);
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
||||||
|
|
||||||
return matchResult;
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim: set ts=2 et sw=2 tw=80: */
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
#ifndef _xpcAccessiblePivot_H_
|
|
||||||
#define _xpcAccessiblePivot_H_
|
|
||||||
|
|
||||||
#include "nsIAccessiblePivot.h"
|
|
||||||
|
|
||||||
#include "Accessible.h"
|
|
||||||
#include "nsCycleCollectionParticipant.h"
|
|
||||||
#include "mozilla/Attributes.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class represents an accessible pivot.
|
|
||||||
*/
|
|
||||||
class xpcAccessiblePivot final : public nsIAccessiblePivot {
|
|
||||||
public:
|
|
||||||
explicit xpcAccessiblePivot(nsIAccessible* aRoot);
|
|
||||||
|
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessiblePivot,
|
|
||||||
nsIAccessiblePivot)
|
|
||||||
|
|
||||||
NS_DECL_NSIACCESSIBLEPIVOT
|
|
||||||
|
|
||||||
private:
|
|
||||||
~xpcAccessiblePivot();
|
|
||||||
xpcAccessiblePivot() = delete;
|
|
||||||
xpcAccessiblePivot(const xpcAccessiblePivot&) = delete;
|
|
||||||
void operator=(const xpcAccessiblePivot&) = delete;
|
|
||||||
|
|
||||||
Accessible* Root() { return mRoot ? mRoot->ToInternalGeneric() : nullptr; }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The root accessible.
|
|
||||||
*/
|
|
||||||
RefPtr<nsIAccessible> mRoot;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Загрузка…
Ссылка в новой задаче