Bug 1754905 - Match Pivot API in nsIAccessiblePivot. r=Jamie
This patch does several things: 1. Remove virtual cursor doc accessible member. 2. Remove state-keeping nsAccessiblePivot implementation and replace it with something that is much more similar to the native Pivot interface. 3. Move nsIAccessiblePivot implementation to xpcom and rename it xpcAccessiblePivot. 4. Delete mochitests and introduce browser tests. 5. Make new xpcom implementation work on remote accessibles as well. Differential Revision: https://phabricator.services.mozilla.com/D181813
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
#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"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "DocAccessible-inl.h"
|
#include "DocAccessible-inl.h"
|
||||||
#include "DocAccessibleChild.h"
|
#include "DocAccessibleChild.h"
|
||||||
|
#include "LocalAccessible-inl.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
#include "TextLeafAccessible.h"
|
#include "TextLeafAccessible.h"
|
||||||
#include "TextUpdater.h"
|
#include "TextUpdater.h"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#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,7 +43,6 @@ 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",
|
||||||
|
|||||||
@@ -1,395 +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 "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;
|
|
||||||
}
|
|
||||||
@@ -1,124 +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 _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,7 +464,11 @@ 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,7 +9,6 @@
|
|||||||
|
|
||||||
#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"
|
||||||
@@ -34,14 +33,6 @@ 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'
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#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"
|
||||||
@@ -99,7 +98,6 @@ 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;
|
||||||
@@ -122,7 +120,6 @@ 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()) {
|
||||||
@@ -149,7 +146,6 @@ 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();
|
||||||
@@ -165,7 +161,6 @@ 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)
|
||||||
@@ -501,11 +496,6 @@ void DocAccessible::Shutdown() {
|
|||||||
MOZ_ASSERT(!mIPCDoc);
|
MOZ_ASSERT(!mIPCDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mVirtualCursor) {
|
|
||||||
mVirtualCursor->RemoveObserver(this);
|
|
||||||
mVirtualCursor = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDependentIDsHashes.Clear();
|
mDependentIDsHashes.Clear();
|
||||||
mNodeToAccessibleMap.Clear();
|
mNodeToAccessibleMap.Clear();
|
||||||
|
|
||||||
@@ -723,23 +713,6 @@ 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,8 +6,6 @@
|
|||||||
#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"
|
||||||
|
|
||||||
@@ -19,8 +17,6 @@
|
|||||||
#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 {
|
||||||
@@ -48,13 +44,10 @@ 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;
|
||||||
|
|
||||||
@@ -135,11 +128,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -706,11 +694,6 @@ 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,7 +7,6 @@
|
|||||||
#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"
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#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,11 +63,6 @@ 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,7 +10,6 @@ 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
|
||||||
@@ -21,6 +20,7 @@ interface nsIAccessiblePivotObserver;
|
|||||||
[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,21 +28,6 @@ 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.
|
||||||
@@ -51,14 +36,11 @@ 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.
|
||||||
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
* @return next accessible node that matches rule in preorder.
|
||||||
* (default is true).
|
|
||||||
* @return true on success, false if there are no new nodes to traverse to.
|
|
||||||
*/
|
*/
|
||||||
[optional_argc] boolean moveNext(in nsIAccessibleTraversalRule aRule,
|
[optional_argc] nsIAccessible next(in nsIAccessible aAnchor,
|
||||||
[optional] in nsIAccessible aAnchor,
|
in nsIAccessibleTraversalRule aRule,
|
||||||
[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,
|
||||||
@@ -68,89 +50,38 @@ 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.
|
||||||
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
* @return previous accessible node that matches rule in preorder.
|
||||||
* (default is true).
|
|
||||||
* @return true on success, false if there are no new nodes to traverse to.
|
|
||||||
*/
|
*/
|
||||||
[optional_argc] boolean movePrevious(in nsIAccessibleTraversalRule aRule,
|
[optional_argc] nsIAccessible prev(in nsIAccessible aAnchor,
|
||||||
[optional] in nsIAccessible aAnchor,
|
in nsIAccessibleTraversalRule aRule,
|
||||||
[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.
|
||||||
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
* @return first accessible node in subtree that matches rule in preorder.
|
||||||
* (default is true).
|
|
||||||
* @return true on success, false if there are no new nodes to traverse to.
|
|
||||||
*/
|
*/
|
||||||
[optional_argc] boolean moveFirst(in nsIAccessibleTraversalRule aRule,
|
nsIAccessible first(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.
|
||||||
* @param aIsFromUserInput [in] the pivot changed because of direct user input
|
* @return last accessible node in subtree that matches rule in preorder.
|
||||||
* (default is true).
|
|
||||||
*/
|
*/
|
||||||
[optional_argc] boolean moveLast(in nsIAccessibleTraversalRule aRule,
|
nsIAccessible last(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 aIgnoreNoMatch [in] don't unset position if no object was found
|
* @param aRule [in] raversal rule to use.
|
||||||
* at point.
|
* @return highest accessible in subtree that matches rule at given 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.
|
|
||||||
*/
|
*/
|
||||||
[optional_argc] boolean moveToPoint(in nsIAccessibleTraversalRule aRule,
|
nsIAccessible atPoint(in long aX, in long aY,
|
||||||
in long aX, in long aY,
|
in nsIAccessibleTraversalRule aRule);
|
||||||
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)]
|
||||||
@@ -163,29 +94,6 @@ 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,6 +14,7 @@
|
|||||||
#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,6 +40,7 @@ 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,7 +46,6 @@ 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]
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +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";
|
|
||||||
|
|
||||||
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 }
|
|
||||||
);
|
|
||||||
10
accessible/tests/browser/pivot/browser.ini
Normal file
10
accessible/tests/browser/pivot/browser.ini
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[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]
|
||||||
103
accessible/tests/browser/pivot/browser_pivot.js
Normal file
103
accessible/tests/browser/pivot/browser_pivot.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/* 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 }
|
||||||
|
);
|
||||||
122
accessible/tests/browser/pivot/head.js
Normal file
122
accessible/tests/browser/pivot/head.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/* 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,7 +18,6 @@ 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",
|
||||||
|
|||||||
@@ -1,575 +0,0 @@
|
|||||||
/* 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");
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[DEFAULT]
|
|
||||||
support-files =
|
|
||||||
doc_virtualcursor.html
|
|
||||||
!/accessible/tests/mochitest/*.js
|
|
||||||
|
|
||||||
[test_virtualcursor.html]
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
<!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,6 +14,7 @@ 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 "nsAccessiblePivot.h"
|
#include "xpcAccessiblePivot.h"
|
||||||
#include "nsAccessibilityService.h"
|
#include "nsAccessibilityService.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "xpcAccessibleApplication.h"
|
#include "xpcAccessibleApplication.h"
|
||||||
@@ -236,10 +236,7 @@ xpcAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
|
|||||||
NS_ENSURE_ARG(aRoot);
|
NS_ENSURE_ARG(aRoot);
|
||||||
*aPivot = nullptr;
|
*aPivot = nullptr;
|
||||||
|
|
||||||
LocalAccessible* accessibleRoot = aRoot->ToInternalAccessible();
|
xpcAccessiblePivot* pivot = new xpcAccessiblePivot(aRoot);
|
||||||
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,17 +132,6 @@ 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,7 +46,6 @@ 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.
|
||||||
|
|||||||
155
accessible/xpcom/xpcAccessiblePivot.cpp
Normal file
155
accessible/xpcom/xpcAccessiblePivot.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/* -*- 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 "xpcAccessibleDocument.h"
|
||||||
|
|
||||||
|
#include "Pivot.h"
|
||||||
|
|
||||||
|
using namespace mozilla::a11y;
|
||||||
|
|
||||||
|
using mozilla::DebugOnly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
47
accessible/xpcom/xpcAccessiblePivot.h
Normal file
47
accessible/xpcom/xpcAccessiblePivot.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/* -*- 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"
|
||||||
|
#include "xpcAccessible.h"
|
||||||
|
|
||||||
|
namespace mozilla::a11y {
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla::a11y
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user