Backed out changeset 8be4693aecbb (bug 1754905) for causing bustages in xpcAccessiblePivot.h. CLOSED TREE

This commit is contained in:
Stanca Serban 2023-06-24 03:22:31 +03:00
parent 3f23020c5c
commit f0967c3305
32 changed files with 1517 additions and 464 deletions

View File

@ -20,7 +20,6 @@
#include "nsAccessibilityService.h"
#include "nsEventShell.h"
#include "nsIAccessibleAnnouncementEvent.h"
#include "nsIAccessiblePivot.h"
#include "nsAccUtils.h"
#include "nsTextEquivUtils.h"
#include "nsWhitespaceTokenizer.h"
@ -134,8 +133,11 @@ nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
RefPtr<AccessibleWrap> newPosition =
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
if (sessionAcc && newPosition) {
MOZ_ASSERT(vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT);
sessionAcc->SendHoverEnterEvent(newPosition);
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(newPosition);
} else {
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
}
}
break;
}

View File

@ -182,8 +182,11 @@ void a11y::ProxyVirtualCursorChangeEvent(RemoteAccessible* aTarget,
return;
}
MOZ_ASSERT(aReason == nsIAccessiblePivot::REASON_POINT);
sessionAcc->SendHoverEnterEvent(aNewPosition);
if (aReason == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(aNewPosition);
} else {
sessionAcc->SendAccessibilityFocusedEvent(aNewPosition);
}
}
void a11y::ProxyScrollingEvent(RemoteAccessible* aTarget, uint32_t aEventType,

View File

@ -10,7 +10,6 @@
#include "RemoteAccessible.h"
#include "DocAccessible.h"
#include "nsAccUtils.h"
#include "nsIAccessiblePivot.h"
#include "mozilla/a11y/Accessible.h"
#include "mozilla/a11y/HyperTextAccessibleBase.h"

View File

@ -43,6 +43,7 @@ UNIFIED_SOURCES += [
"FocusManager.cpp",
"NotificationController.cpp",
"nsAccessibilityService.cpp",
"nsAccessiblePivot.cpp",
"nsAccUtils.cpp",
"nsCoreUtils.cpp",
"nsEventShell.cpp",

View File

@ -0,0 +1,395 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAccessiblePivot.h"
#include "HyperTextAccessible.h"
#include "nsAccUtils.h"
#include "States.h"
#include "Pivot.h"
#include "xpcAccessibleDocument.h"
#include "nsTArray.h"
#include "mozilla/Maybe.h"
using namespace mozilla::a11y;
using mozilla::DebugOnly;
using mozilla::Maybe;
/**
* An object that stores a given traversal rule during the pivot movement.
*/
class RuleCache : public PivotRule {
public:
explicit RuleCache(nsIAccessibleTraversalRule* aRule)
: mRule(aRule), mPreFilter{0} {}
~RuleCache() {}
virtual uint16_t Match(Accessible* aAcc) override;
private:
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
Maybe<nsTArray<uint32_t>> mAcceptRoles;
uint32_t mPreFilter;
};
////////////////////////////////////////////////////////////////////////////////
// nsAccessiblePivot
nsAccessiblePivot::nsAccessiblePivot(LocalAccessible* aRoot)
: mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr) {
NS_ASSERTION(aRoot, "A root accessible is required");
}
nsAccessiblePivot::~nsAccessiblePivot() {}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
////////////////////////////////////////////////////////////////////////////////
// nsIAccessiblePivot
NS_IMETHODIMP
nsAccessiblePivot::GetRoot(nsIAccessible** aRoot) {
NS_ENSURE_ARG_POINTER(aRoot);
NS_IF_ADDREF(*aRoot = ToXPC(mRoot));
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetPosition(nsIAccessible** aPosition) {
NS_ENSURE_ARG_POINTER(aPosition);
NS_IF_ADDREF(*aPosition = ToXPC(mPosition));
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) {
RefPtr<LocalAccessible> position = nullptr;
if (aPosition) {
position = aPosition->ToInternalAccessible();
if (!position || !IsDescendantOf(position, GetActiveRoot())) {
return NS_ERROR_INVALID_ARG;
}
}
// Swap old position with new position, saves us an AddRef/Release.
mPosition.swap(position);
NotifyOfPivotChange(position, nsIAccessiblePivot::REASON_NONE, false);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot) {
NS_ENSURE_ARG_POINTER(aModalRoot);
NS_IF_ADDREF(*aModalRoot = ToXPC(mModalRoot));
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot) {
LocalAccessible* modalRoot = nullptr;
if (aModalRoot) {
modalRoot = aModalRoot->ToInternalAccessible();
if (!modalRoot || !IsDescendantOf(modalRoot, mRoot)) {
return NS_ERROR_INVALID_ARG;
}
}
mModalRoot = modalRoot;
return NS_OK;
}
// Traversal functions
NS_IMETHODIMP
nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
nsIAccessible* aAnchor, bool aIncludeStart,
bool aIsFromUserInput, uint8_t aArgc,
bool* aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
*aResult = false;
LocalAccessible* anchor = mPosition;
if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
if (anchor &&
(anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot()))) {
return NS_ERROR_NOT_IN_TREE;
}
Pivot pivot(GetActiveRoot());
RuleCache rule(aRule);
Accessible* newPos =
pivot.Next(anchor, rule, (aArgc > 1) ? aIncludeStart : false);
if (newPos && newPos->IsLocal()) {
*aResult =
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_NEXT,
(aArgc > 2) ? aIsFromUserInput : true);
} else if (newPos && newPos->IsRemote()) {
// we shouldn't ever end up with a proxy here, but if we do for some
// reason something is wrong. we should still return OK if we're null
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
nsIAccessible* aAnchor, bool aIncludeStart,
bool aIsFromUserInput, uint8_t aArgc,
bool* aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
*aResult = false;
LocalAccessible* anchor = mPosition;
if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
if (anchor &&
(anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot()))) {
return NS_ERROR_NOT_IN_TREE;
}
Pivot pivot(GetActiveRoot());
RuleCache rule(aRule);
Accessible* newPos =
pivot.Prev(anchor, rule, (aArgc > 1) ? aIncludeStart : false);
if (newPos && newPos->IsLocal()) {
*aResult =
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_PREV,
(aArgc > 2) ? aIsFromUserInput : true);
} else if (newPos && newPos->IsRemote()) {
// we shouldn't ever end up with a proxy here, but if we do for some
// reason something is wrong. we should still return OK if we're null
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule,
bool aIsFromUserInput, uint8_t aArgc,
bool* aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
LocalAccessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
Pivot pivot(GetActiveRoot());
RuleCache rule(aRule);
Accessible* newPos = pivot.First(rule);
if (newPos && newPos->IsLocal()) {
*aResult =
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_FIRST,
(aArgc > 0) ? aIsFromUserInput : true);
} else if (newPos && newPos->IsRemote()) {
// we shouldn't ever end up with a proxy here, but if we do for some
// reason something is wrong. we should still return OK if we're null
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
bool aIsFromUserInput, uint8_t aArgc,
bool* aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
LocalAccessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
Pivot pivot(root);
RuleCache rule(aRule);
Accessible* newPos = pivot.Last(rule);
if (newPos && newPos->IsLocal()) {
*aResult =
MovePivotInternal(newPos->AsLocal(), nsIAccessiblePivot::REASON_LAST,
(aArgc > 0) ? aIsFromUserInput : true);
} else if (newPos && newPos->IsRemote()) {
// we shouldn't ever end up with a proxy here, but if we do for some
// reason something is wrong. we should still return OK if we're null
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, int32_t aX,
int32_t aY, bool aIgnoreNoMatch,
bool aIsFromUserInput, uint8_t aArgc,
bool* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aRule);
*aResult = false;
LocalAccessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
RuleCache rule(aRule);
Pivot pivot(root);
Accessible* newPos = pivot.AtPoint(aX, aY, rule);
if ((newPos && newPos->IsLocal()) ||
!aIgnoreNoMatch) { // TODO does this need a proxy check?
*aResult = MovePivotInternal(newPos ? newPos->AsLocal() : nullptr,
nsIAccessiblePivot::REASON_POINT,
(aArgc > 0) ? aIsFromUserInput : true);
} else if (newPos && newPos->IsRemote()) {
// we shouldn't ever end up with a proxy here, but if we do for some
// reason something is wrong. we should still return OK if we're null
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// Observer functions
NS_IMETHODIMP
nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver) {
NS_ENSURE_ARG(aObserver);
mObservers.AppendElement(aObserver);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver) {
NS_ENSURE_ARG(aObserver);
return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
}
// Private utility methods
bool nsAccessiblePivot::IsDescendantOf(LocalAccessible* aAccessible,
LocalAccessible* aAncestor) {
if (!aAncestor || aAncestor->IsDefunct()) return false;
// XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
LocalAccessible* accessible = aAccessible;
do {
if (accessible == aAncestor) return true;
} while ((accessible = accessible->LocalParent()));
return false;
}
bool nsAccessiblePivot::MovePivotInternal(LocalAccessible* aPosition,
PivotMoveReason aReason,
bool aIsFromUserInput) {
RefPtr<LocalAccessible> oldPosition = std::move(mPosition);
mPosition = aPosition;
return NotifyOfPivotChange(oldPosition, aReason, aIsFromUserInput);
}
bool nsAccessiblePivot::NotifyOfPivotChange(LocalAccessible* aOldPosition,
int16_t aReason,
bool aIsFromUserInput) {
if (aOldPosition == mPosition) {
return false;
}
nsCOMPtr<nsIAccessible> xpcOldPos = ToXPC(aOldPosition); // death grip
for (nsIAccessiblePivotObserver* obs : mObservers.ForwardRange()) {
obs->OnPivotChanged(this, xpcOldPos, ToXPC(mPosition), aReason,
aIsFromUserInput);
}
return true;
}
uint16_t RuleCache::Match(Accessible* aAcc) {
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (!mAcceptRoles) {
mAcceptRoles.emplace();
DebugOnly<nsresult> rv = mRule->GetMatchRoles(*mAcceptRoles);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = mRule->GetPreFilter(&mPreFilter);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
if (mPreFilter) {
uint64_t state = aAcc->State();
if ((nsIAccessibleTraversalRule::PREFILTER_PLATFORM_PRUNED & mPreFilter) &&
nsAccUtils::MustPrune(aAcc)) {
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
(state & states::INVISIBLE)) {
return result;
}
if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
(state & states::OFFSCREEN)) {
return result;
}
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
!(state & states::FOCUSABLE)) {
return result;
}
if (nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) {
if (aAcc->Opacity() == 0.0f) {
return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
}
}
if (mAcceptRoles->Length() > 0) {
uint32_t accessibleRole = aAcc->Role();
bool matchesRole = false;
for (uint32_t idx = 0; idx < mAcceptRoles->Length(); idx++) {
matchesRole = mAcceptRoles->ElementAt(idx) == accessibleRole;
if (matchesRole) break;
}
if (!matchesRole) {
return result;
}
}
uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return result | matchResult;
}

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsAccessiblePivot_H_
#define _nsAccessiblePivot_H_
#include "nsIAccessiblePivot.h"
#include "LocalAccessible-inl.h"
#include "nsTObserverArray.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
class RuleCache;
/**
* Class represents an accessible pivot.
*/
class nsAccessiblePivot final : public nsIAccessiblePivot {
public:
typedef mozilla::a11y::LocalAccessible LocalAccessible;
explicit nsAccessiblePivot(LocalAccessible* aRoot);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAccessiblePivot,
nsIAccessiblePivot)
NS_DECL_NSIACCESSIBLEPIVOT
/*
* A simple getter for the pivot's position.
*/
LocalAccessible* Position() { return mPosition; }
private:
~nsAccessiblePivot();
nsAccessiblePivot() = delete;
nsAccessiblePivot(const nsAccessiblePivot&) = delete;
void operator=(const nsAccessiblePivot&) = delete;
/*
* Notify all observers on a pivot change. Return true if it has changed and
* observers have been notified.
*/
bool NotifyOfPivotChange(LocalAccessible* aOldPosition,
PivotMoveReason aReason, bool aIsFromUserInput);
/*
* Check to see that the given accessible is a descendant of given ancestor
*/
bool IsDescendantOf(LocalAccessible* aAccessible, LocalAccessible* aAncestor);
/*
* Search in preorder for the first accessible to match the rule.
*/
LocalAccessible* SearchForward(LocalAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool aSearchCurrent, nsresult* aResult);
/*
* Reverse search in preorder for the first accessible to match the rule.
*/
LocalAccessible* SearchBackward(LocalAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool aSearchCurrent, nsresult* aResult);
/*
* Get the effective root for this pivot, either the true root or modal root.
*/
LocalAccessible* GetActiveRoot() const {
if (mModalRoot) {
NS_ENSURE_FALSE(mModalRoot->IsDefunct(), mRoot);
return mModalRoot;
}
return mRoot;
}
/*
* Update the pivot, and notify observers. Return true if it moved.
*/
bool MovePivotInternal(LocalAccessible* aPosition, PivotMoveReason aReason,
bool aIsFromUserInput);
/*
* Get initial node we should start a search from with a given rule.
*
* When we do a move operation from one position to another,
* the initial position can be inside of a subtree that is ignored by
* the given rule. We need to step out of the ignored subtree and start
* the search from there.
*
*/
LocalAccessible* AdjustStartPosition(LocalAccessible* aAccessible,
RuleCache& aCache,
uint16_t* aFilterResult,
nsresult* aResult);
/*
* The root accessible.
*/
RefPtr<LocalAccessible> mRoot;
/*
* The temporary modal root accessible.
*/
RefPtr<LocalAccessible> mModalRoot;
/*
* The current pivot position.
*/
RefPtr<LocalAccessible> mPosition;
/*
* The list of pivot-changed observers.
*/
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
};
#endif

View File

@ -464,11 +464,7 @@ void Accessible::DebugDescription(nsCString& aDesc) const {
void Accessible::DebugPrint(const char* aPrefix,
const Accessible* aAccessible) {
nsAutoCString desc;
if (aAccessible) {
aAccessible->DebugDescription(desc);
} else {
desc.AssignLiteral("[null]");
}
aAccessible->DebugDescription(desc);
# if defined(ANDROID)
printf_stderr("%s %s\n", aPrefix, desc.get());
# else

View File

@ -9,6 +9,7 @@
#include "DocAccessible.h"
#include "nsAccessibilityService.h"
#include "nsAccessiblePivot.h"
#include "NotificationController.h"
#include "States.h"
#include "nsIScrollableFrame.h"
@ -33,6 +34,14 @@ inline LocalAccessible* DocAccessible::AccessibleOrTrueContainer(
return container;
}
inline nsIAccessiblePivot* DocAccessible::VirtualCursor() {
if (!mVirtualCursor) {
mVirtualCursor = new nsAccessiblePivot(this);
mVirtualCursor->AddObserver(this);
}
return mVirtualCursor;
}
inline bool DocAccessible::IsContentLoaded() const {
// eDOMLoaded flag check is used for error pages as workaround to make this
// method return correct result since error pages do not receive 'pageshow'

View File

@ -14,6 +14,7 @@
#include "HTMLImageMapAccessible.h"
#include "mozilla/ProfilerMarkers.h"
#include "nsAccCache.h"
#include "nsAccessiblePivot.h"
#include "nsAccUtils.h"
#include "nsEventShell.h"
#include "nsIIOService.h"
@ -98,6 +99,7 @@ DocAccessible::DocAccessible(dom::Document* aDocument,
mViewportCacheDirty(false),
mLoadEventType(0),
mPrevStateBits(0),
mVirtualCursor(nullptr),
mPresShell(aPresShell),
mIPCDoc(nullptr) {
mGenericTypes |= eDocument;
@ -120,6 +122,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible,
LocalAccessible)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
for (const auto& hashEntry : tmp->mDependentIDsHashes.Values()) {
for (const auto& providers : hashEntry->Values()) {
@ -146,6 +149,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, LocalAccessible)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
tmp->mDependentIDsHashes.Clear();
tmp->mNodeToAccessibleMap.Clear();
@ -161,6 +165,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocAccessible)
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
NS_INTERFACE_MAP_END_INHERITING(HyperTextAccessible)
NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
@ -496,6 +501,11 @@ void DocAccessible::Shutdown() {
MOZ_ASSERT(!mIPCDoc);
}
if (mVirtualCursor) {
mVirtualCursor->RemoveObserver(this);
mVirtualCursor = nullptr;
}
mDependentIDsHashes.Clear();
mNodeToAccessibleMap.Clear();
@ -713,6 +723,23 @@ std::pair<nsPoint, nsRect> DocAccessible::ComputeScrollData(
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

View File

@ -6,6 +6,8 @@
#ifndef mozilla_a11y_DocAccessible_h__
#define mozilla_a11y_DocAccessible_h__
#include "nsIAccessiblePivot.h"
#include "HyperTextAccessibleWrap.h"
#include "AccEvent.h"
@ -17,6 +19,8 @@
#include "nsTHashSet.h"
#include "nsWeakReference.h"
class nsAccessiblePivot;
const uint32_t kDefaultCacheLength = 128;
namespace mozilla {
@ -44,10 +48,13 @@ class TNotification;
*/
class DocAccessible : public HyperTextAccessibleWrap,
public nsIDocumentObserver,
public nsSupportsWeakReference {
public nsSupportsWeakReference,
public nsIAccessiblePivotObserver {
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible)
NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
protected:
typedef mozilla::dom::Document Document;
@ -128,6 +135,11 @@ class DocAccessible : public HyperTextAccessibleWrap,
*/
void QueueCacheUpdateForDependentRelations(LocalAccessible* aAcc);
/**
* Return virtual cursor associated with the document.
*/
nsIAccessiblePivot* VirtualCursor();
/**
* Returns true if the instance has shutdown.
*/
@ -694,6 +706,11 @@ class DocAccessible : public HyperTextAccessibleWrap,
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.
*/

View File

@ -7,6 +7,7 @@
#include "HyperTextAccessible-inl.h"
#include "nsAccessibilityService.h"
#include "nsAccessiblePivot.h"
#include "nsIAccessibleTypes.h"
#include "AccAttributes.h"
#include "DocAccessible.h"

View File

@ -20,6 +20,7 @@
#include "nsAccUtils.h"
#include "nsAccessibilityService.h"
#include "ApplicationAccessible.h"
#include "nsAccessiblePivot.h"
#include "nsGenericHTMLElement.h"
#include "NotificationController.h"
#include "nsEventShell.h"

View File

@ -63,6 +63,11 @@ interface nsIAccessibleDocument : nsISupports
*/
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.
*/

View File

@ -10,6 +10,7 @@ typedef short PivotMoveReason;
interface nsIAccessible;
interface nsIAccessibleTraversalRule;
interface nsIAccessiblePivotObserver;
/**
* The pivot interface encapsulates a reference to a single place in an accessible
@ -20,7 +21,6 @@ interface nsIAccessibleTraversalRule;
[scriptable, uuid(81fe5144-059b-42db-bd3a-f6ce3158d5e9)]
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_NEXT = 1;
const PivotMoveReason REASON_PREV = 2;
@ -28,6 +28,21 @@ interface nsIAccessiblePivot : nsISupports
const PivotMoveReason REASON_LAST = 4;
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,
* complying to given traversal rule.
@ -36,11 +51,14 @@ interface nsIAccessiblePivot : nsISupports
* @param aAnchor [in] accessible to start search from, if not provided,
* current position will be used.
* @param aIncludeStart [in] include anchor accessible in search.
* @return next accessible node that matches rule in preorder.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
* @return true on success, false if there are no new nodes to traverse to.
*/
[optional_argc] nsIAccessible next(in nsIAccessible aAnchor,
in nsIAccessibleTraversalRule aRule,
[optional] in boolean aIncludeStart);
[optional_argc] boolean moveNext(in nsIAccessibleTraversalRule aRule,
[optional] in nsIAccessible aAnchor,
[optional] in boolean aIncludeStart,
[optional] in boolean aIsFromUserInput);
/**
* Move pivot to previous object, from current position or given anchor,
@ -50,38 +68,89 @@ interface nsIAccessiblePivot : nsISupports
* @param aAnchor [in] accessible to start search from, if not provided,
* current position will be used.
* @param aIncludeStart [in] include anchor accessible in search.
* @return previous accessible node that matches rule in preorder.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
* @return true on success, false if there are no new nodes to traverse to.
*/
[optional_argc] nsIAccessible prev(in nsIAccessible aAnchor,
in nsIAccessibleTraversalRule aRule,
[optional] in boolean aIncludeStart);
[optional_argc] boolean movePrevious(in nsIAccessibleTraversalRule aRule,
[optional] in nsIAccessible aAnchor,
[optional] in boolean aIncludeStart,
[optional] in boolean aIsFromUserInput);
/**
* Move pivot to first object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return first accessible node in subtree that matches rule in preorder.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
* @return true on success, false if there are no new nodes to traverse to.
*/
nsIAccessible first(in nsIAccessibleTraversalRule aRule);
[optional_argc] boolean moveFirst(in nsIAccessibleTraversalRule aRule,
[optional] in boolean aIsFromUserInput);
/**
* Move pivot to last object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return last accessible node in subtree that matches rule in preorder.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
*/
nsIAccessible last(in nsIAccessibleTraversalRule aRule);
[optional_argc] boolean moveLast(in nsIAccessibleTraversalRule aRule,
[optional] in boolean aIsFromUserInput);
/**
* Move pivot to given coordinate in screen pixels.
*
* @param aRule [in] raversal rule to use.
* @param aX [in] screen's x coordinate
* @param aY [in] screen's y coordinate
* @param aRule [in] raversal rule to use.
* @return highest accessible in subtree that matches rule at given point.
* @param aIgnoreNoMatch [in] don't unset position if no object was found
* at point.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
* @return true on success, false if the pivot has not been moved.
*/
nsIAccessible atPoint(in long aX, in long aY,
in nsIAccessibleTraversalRule aRule);
[optional_argc] boolean moveToPoint(in nsIAccessibleTraversalRule aRule,
in long aX, in long aY,
in boolean aIgnoreNoMatch,
[optional] in boolean aIsFromUserInput);
/**
* Add an observer for pivot changes.
*
* @param aObserver [in] the observer object to be notified of pivot changes.
*/
void addObserver(in nsIAccessiblePivotObserver aObserver);
/**
* Remove an observer for pivot changes.
*
* @param aObserver [in] the observer object to remove from being notified.
*/
void removeObserver(in nsIAccessiblePivotObserver aObserver);
};
/**
* An observer interface for pivot changes.
*/
[scriptable, uuid(6006e502-3861-49bd-aba1-fa6d2e74e237)]
interface nsIAccessiblePivotObserver : nsISupports
{
/**
* Called when the pivot changes.
*
* @param aPivot [in] the pivot that has changed.
* @param aOldAccessible [in] the old pivot position before the change,
* or null.
* @param aReason [in] the reason for the pivot change.
* @param aIsFromUserInput [in] the pivot changed because of direct user input
* (default is true).
*/
void onPivotChanged(in nsIAccessiblePivot aPivot,
in nsIAccessible aOldAccessible,
in nsIAccessible aNewAccessible,
in PivotMoveReason aReason,
in boolean aIsFromUserInput);
};
[scriptable, uuid(e197460d-1eff-4247-b4bb-a43be1840dae)]
@ -94,6 +163,29 @@ interface nsIAccessibleTraversalRule : nsISupports
/* Don't traverse accessibles children */
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
*

View File

@ -14,7 +14,6 @@
#include "mozilla/a11y/TableAccessible.h"
#include "mozilla/a11y/TableCellAccessible.h"
#include "nsAccessibilityService.h"
#include "nsIAccessiblePivot.h"
#include "XULTreeAccessible.h"
#include "Pivot.h"
#include "nsAccUtils.h"

View File

@ -40,7 +40,6 @@ BROWSER_CHROME_MANIFESTS += [
"tests/browser/general/browser.ini",
"tests/browser/hittest/browser.ini",
"tests/browser/mac/browser.ini",
"tests/browser/pivot/browser.ini",
"tests/browser/role/browser.ini",
"tests/browser/scroll/browser.ini",
"tests/browser/selectable/browser.ini",

View File

@ -46,6 +46,7 @@ skip-if = os == 'win' # Bug 1288839
[browser_events_show.js]
[browser_events_statechange.js]
[browser_events_textchange.js]
[browser_events_vcchange.js]
[browser_language.js]

View File

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
addAccessibleTask(
`
<p id="p1">abc</p>
<input id="input1" value="input" />`,
async function (browser) {
let onVCChanged = waitForEvent(
EVENT_VIRTUALCURSOR_CHANGED,
matchContentDoc
);
await invokeContentTask(browser, [], () => {
const { CommonUtils } = ChromeUtils.importESModule(
"chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs"
);
let vc = CommonUtils.getAccessible(
content.document,
Ci.nsIAccessibleDocument
).virtualCursor;
vc.position = CommonUtils.getAccessible(
"p1",
null,
null,
null,
content.document
);
});
let vccEvent = (await onVCChanged).QueryInterface(
nsIAccessibleVirtualCursorChangeEvent
);
is(vccEvent.newAccessible.id, "p1", "New position is correct");
ok(!vccEvent.isFromUserInput, "not user initiated");
onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc);
onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc);
await invokeContentTask(browser, [], () => {
const { CommonUtils } = ChromeUtils.importESModule(
"chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs"
);
let vc = CommonUtils.getAccessible(
content.document,
Ci.nsIAccessibleDocument
).virtualCursor;
vc.position = CommonUtils.getAccessible(
"input1",
null,
null,
null,
content.document
);
});
vccEvent = (await onVCChanged).QueryInterface(
nsIAccessibleVirtualCursorChangeEvent
);
isnot(vccEvent.oldAccessible, vccEvent.newAccessible, "positions differ");
is(vccEvent.oldAccessible.id, "p1", "Old position is correct");
is(vccEvent.newAccessible.id, "input1", "New position is correct");
ok(!vccEvent.isFromUserInput, "not user initiated");
},
{ iframe: true, remoteIframe: true }
);

View File

@ -1,10 +0,0 @@
[DEFAULT]
subsuite = a11y
support-files =
!/accessible/tests/browser/shared-head.js
head.js
!/accessible/tests/mochitest/*.js
prefs =
javascript.options.asyncstack_capture_debuggee_only=false
[browser_pivot.js]

View File

@ -1,103 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* Tests Pivot API
*/
addAccessibleTask(
`
<h1 id="heading-1-1">Main Title</h1>
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
<p id="paragraph-1">
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
leo, id <a href="#">semper</a> nulla.
</p>
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
<p id="paragraph-2" aria-hidden="">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<p id="paragraph-3" aria-hidden="true">
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
You know, the <a href="#">singer-songwriter</a>.
</p>
<p style="opacity: 0;" id="paragraph-4">
This is completely transparent
</p>
<iframe
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
</iframe>
<div id="hide-me">Hide me</div>
<p id="links" aria-hidden="false">
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
</p>
<ul>
<li>Hello<span> </span></li>
<li>World</li>
</ul>
`,
async function (browser, docAcc) {
let pivot = gAccService.createAccessiblePivot(docAcc);
testPivotSequence(pivot, HeadersTraversalRule, [
"heading-1-1",
"heading-2-2",
]);
testPivotSequence(pivot, ObjectTraversalRule, [
"Main Title",
"Lorem ipsum ",
"dolor",
" sit amet. Integer vitae urna leo, id ",
"semper",
" nulla. ",
"Second Section Title",
"Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
"An ",
"embedded",
" document.",
"Hide me",
"Link 1",
"Link 2",
"Link 3",
"Hello",
"World",
]);
let hideMeAcc = findAccessibleChildByID(docAcc, "hide-me");
let onHide = waitForEvent(EVENT_HIDE, hideMeAcc);
invokeContentTask(browser, [], () => {
content.document.getElementById("hide-me").remove();
});
await onHide;
testFailsWithNotInTree(
() => pivot.next(hideMeAcc, ObjectTraversalRule),
"moveNext from defunct accessible should fail"
);
let linksAcc = findAccessibleChildByID(docAcc, "links");
let removedRootPivot = gAccService.createAccessiblePivot(linksAcc);
onHide = waitForEvent(EVENT_HIDE, linksAcc);
invokeContentTask(browser, [], () => {
content.document.getElementById("links").remove();
});
await onHide;
testFailsWithNotInTree(
() => removedRootPivot.last(ObjectTraversalRule),
"moveLast with pivot with defunct root should fail"
);
let [x, y] = getBounds(findAccessibleChildByID(docAcc, "heading-1-1"));
let hitacc = pivot.atPoint(x + 1, y + 1, HeadersTraversalRule);
is(getIdOrName(hitacc), "heading-1-1", "Matching accessible at point");
hitacc = pivot.atPoint(x - 1, y - 1, HeadersTraversalRule);
ok(!hitacc, "No heading at given point");
},
{ iframe: true, remoteIframe: true, topLevel: true, chrome: true }
);

View File

@ -1,122 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* exported HeadersTraversalRule, ObjectTraversalRule, testPivotSequence, testFailsWithNotInTree */
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
this
);
/* import-globals-from ../../mochitest/layout.js */
/* import-globals-from ../../mochitest/role.js */
/* import-globals-from ../../mochitest/states.js */
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR },
{ name: "states.js", dir: MOCHITESTS_DIR },
{ name: "role.js", dir: MOCHITESTS_DIR },
{ name: "layout.js", dir: MOCHITESTS_DIR }
);
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
const NS_ERROR_NOT_IN_TREE = 0x80780026;
// //////////////////////////////////////////////////////////////////////////////
// Traversal rules
/**
* Rule object to traverse all focusable nodes and text nodes.
*/
const HeadersTraversalRule = {
match(acc) {
return acc.role == ROLE_HEADING ? FILTER_MATCH : FILTER_IGNORE;
},
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
};
/**
* Traversal rule for all focusable nodes or leafs.
*/
const ObjectTraversalRule = {
match(acc) {
let [state, extstate] = getStates(acc);
if (state & STATE_INVISIBLE) {
return FILTER_IGNORE;
}
if ((extstate & EXT_STATE_OPAQUE) == 0) {
return FILTER_IGNORE | FILTER_IGNORE_SUBTREE;
}
let rv = FILTER_IGNORE;
let role = acc.role;
if (
hasState(acc, STATE_FOCUSABLE) &&
role != ROLE_DOCUMENT &&
role != ROLE_INTERNAL_FRAME
) {
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
} else if (
acc.childCount == 0 &&
role != ROLE_LISTITEM_MARKER &&
acc.name.trim()
) {
rv = FILTER_MATCH;
}
return rv;
},
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
};
function getIdOrName(acc) {
let id = getAccessibleDOMNodeID(acc);
if (id) {
return id;
}
return acc.name;
}
function* pivotNextGenerator(pivot, rule) {
for (let acc = pivot.first(rule); acc; acc = pivot.next(acc, rule)) {
yield acc;
}
}
function* pivotPreviousGenerator(pivot, rule) {
for (let acc = pivot.last(rule); acc; acc = pivot.prev(acc, rule)) {
yield acc;
}
}
function testPivotSequence(pivot, rule, expectedSequence) {
is(
JSON.stringify([...pivotNextGenerator(pivot, rule)].map(getIdOrName)),
JSON.stringify(expectedSequence),
"Forward pivot sequence is correct"
);
is(
JSON.stringify([...pivotPreviousGenerator(pivot, rule)].map(getIdOrName)),
JSON.stringify([...expectedSequence].reverse()),
"Reverse pivot sequence is correct"
);
}
function testFailsWithNotInTree(func, msg) {
try {
func();
ok(false, msg);
} catch (x) {
is(x.result, NS_ERROR_NOT_IN_TREE, `Expecting NOT_IN_TREE: ${msg}`);
}
}

View File

@ -18,6 +18,7 @@ A11Y_MANIFESTS += [
"hyperlink/a11y.ini",
"hypertext/a11y.ini",
"name/a11y.ini",
"pivot/a11y.ini",
"relations/a11y.ini",
"role/a11y.ini",
"scroll/a11y.ini",

View File

@ -0,0 +1,575 @@
/* import-globals-from common.js */
/* import-globals-from events.js */
/* import-globals-from role.js */
/* import-globals-from states.js */
/* import-globals-from text.js */
// //////////////////////////////////////////////////////////////////////////////
// Constants
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
const PREFILTER_TRANSPARENT = nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
const NS_ERROR_NOT_IN_TREE = 0x80780026;
const NS_ERROR_INVALID_ARG = 0x80070057;
// //////////////////////////////////////////////////////////////////////////////
// Traversal rules
/**
* Rule object to traverse all focusable nodes and text nodes.
*/
var HeadersTraversalRule = {
getMatchRoles() {
return [ROLE_HEADING];
},
preFilter: PREFILTER_INVISIBLE,
match(aAccessible) {
return FILTER_MATCH;
},
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
};
/**
* Traversal rule for all focusable nodes or leafs.
*/
var ObjectTraversalRule = {
getMatchRoles() {
return [];
},
preFilter: PREFILTER_INVISIBLE | PREFILTER_TRANSPARENT,
match(aAccessible) {
var rv = FILTER_IGNORE;
var role = aAccessible.role;
if (
hasState(aAccessible, STATE_FOCUSABLE) &&
role != ROLE_DOCUMENT &&
role != ROLE_INTERNAL_FRAME
) {
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
} else if (
aAccessible.childCount == 0 &&
role != ROLE_LISTITEM_MARKER &&
aAccessible.name.trim()
) {
rv = FILTER_MATCH;
}
return rv;
},
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
};
// //////////////////////////////////////////////////////////////////////////////
// Virtual state invokers and checkers
/**
* A checker for virtual cursor changed events.
*/
function VCChangedChecker(
aDocAcc,
aIdOrNameOrAcc,
aTextOffsets,
aPivotMoveMethod,
aIsFromUserInput
) {
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
this.match = function VCChangedChecker_match(aEvent) {
var event = null;
try {
event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
} catch (e) {
return false;
}
var expectedReason =
VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
nsIAccessiblePivot.REASON_NONE;
return event.reason == expectedReason;
};
this.check = function VCChangedChecker_check(aEvent) {
SimpleTest.info("VCChangedChecker_check");
var event = null;
try {
event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
} catch (e) {
SimpleTest.ok(false, "Does not support correct interface: " + e);
}
var position = aDocAcc.virtualCursor.position;
var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
var nameMatches = position && position.name == aIdOrNameOrAcc;
var accMatches = position == aIdOrNameOrAcc;
SimpleTest.ok(
idMatches || nameMatches || accMatches,
"id or name matches - expecting " +
prettyName(aIdOrNameOrAcc) +
", got '" +
prettyName(position)
);
SimpleTest.is(
aEvent.isFromUserInput,
aIsFromUserInput,
"Expected user input is " + aIsFromUserInput + "\n"
);
SimpleTest.is(
event.newAccessible,
position,
"new position in event is incorrect"
);
if (aTextOffsets) {
SimpleTest.is(
aDocAcc.virtualCursor.startOffset,
aTextOffsets[0],
"wrong start offset"
);
SimpleTest.is(
aDocAcc.virtualCursor.endOffset,
aTextOffsets[1],
"wrong end offset"
);
SimpleTest.is(
event.newStartOffset,
aTextOffsets[0],
"wrong start offset in event"
);
SimpleTest.is(
event.newEndOffset,
aTextOffsets[1],
"wrong end offset in event"
);
}
var prevPosAndOffset = VCChangedChecker.getPreviousPosAndOffset(
aDocAcc.virtualCursor
);
if (prevPosAndOffset) {
SimpleTest.is(
event.oldAccessible,
prevPosAndOffset.position,
"previous position does not match"
);
}
};
}
VCChangedChecker.prevPosAndOffset = {};
VCChangedChecker.storePreviousPosAndOffset = function storePreviousPosAndOffset(
aPivot
) {
VCChangedChecker.prevPosAndOffset[aPivot] = {
position: aPivot.position,
startOffset: aPivot.startOffset,
endOffset: aPivot.endOffset,
};
};
VCChangedChecker.getPreviousPosAndOffset = function getPreviousPosAndOffset(
aPivot
) {
return VCChangedChecker.prevPosAndOffset[aPivot];
};
VCChangedChecker.methodReasonMap = {
moveNext: nsIAccessiblePivot.REASON_NEXT,
movePrevious: nsIAccessiblePivot.REASON_PREV,
moveFirst: nsIAccessiblePivot.REASON_FIRST,
moveLast: nsIAccessiblePivot.REASON_LAST,
setTextRange: nsIAccessiblePivot.REASON_NONE,
moveNextByText: nsIAccessiblePivot.REASON_NEXT,
movePreviousByText: nsIAccessiblePivot.REASON_PREV,
moveToPoint: nsIAccessiblePivot.REASON_POINT,
};
/**
* Set a text range in the pivot and wait for virtual cursor change event.
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aTextAccessible [in] accessible to set to virtual cursor's position
* @param aTextOffsets [in] start and end offsets of text range to set in
* virtual cursor.
*/
function setVCRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets) {
this.invoke = function virtualCursorChangedInvoker_invoke() {
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
aDocAcc.virtualCursor.setTextRange(
aTextAccessible,
aTextOffsets[0],
aTextOffsets[1]
);
};
this.getID = function setVCRangeInvoker_getID() {
return (
"Set offset in " +
prettyName(aTextAccessible) +
" to (" +
aTextOffsets[0] +
", " +
aTextOffsets[1] +
")"
);
};
this.eventSeq = [
new VCChangedChecker(
aDocAcc,
aTextAccessible,
aTextOffsets,
"setTextRange",
true
),
];
}
/**
* Move the pivot and wait for virtual cursor change event.
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
* @param aRule [in] traversal rule object
* @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
* virtual cursor to land on after performing move method.
* false if no move is expected.
* @param aIsFromUserInput [in] set user input flag when invoking method, and
* expect it in the event.
*/
function setVCPosInvoker(
aDocAcc,
aPivotMoveMethod,
aRule,
aIdOrNameOrAcc,
aIsFromUserInput
) {
// eslint-disable-next-line mozilla/no-compare-against-boolean-literals
var expectMove = aIdOrNameOrAcc != false;
this.invoke = function virtualCursorChangedInvoker_invoke() {
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
if (aPivotMoveMethod && aRule) {
var moved = false;
switch (aPivotMoveMethod) {
case "moveFirst":
case "moveLast":
moved = aDocAcc.virtualCursor[aPivotMoveMethod](
aRule,
aIsFromUserInput === undefined ? true : aIsFromUserInput
);
break;
case "moveNext":
case "movePrevious":
moved = aDocAcc.virtualCursor[aPivotMoveMethod](
aRule,
aDocAcc.virtualCursor.position,
false,
aIsFromUserInput === undefined ? true : aIsFromUserInput
);
break;
}
SimpleTest.is(
!!moved,
!!expectMove,
"moved pivot with " + aPivotMoveMethod + " to " + aIdOrNameOrAcc
);
} else {
aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
}
};
this.getID = function setVCPosInvoker_getID() {
return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
};
if (expectMove) {
this.eventSeq = [
new VCChangedChecker(
aDocAcc,
aIdOrNameOrAcc,
null,
aPivotMoveMethod,
aIsFromUserInput === undefined ? !!aPivotMoveMethod : aIsFromUserInput
),
];
} else {
this.eventSeq = [];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
];
}
}
/**
* Move the pivot to the position under the point.
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aX [in] screen x coordinate
* @param aY [in] screen y coordinate
* @param aIgnoreNoMatch [in] don't unset position if no object was found at
* point.
* @param aRule [in] traversal rule object
* @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
* virtual cursor to land on after performing move method.
* false if no move is expected.
*/
function moveVCCoordInvoker(
aDocAcc,
aX,
aY,
aIgnoreNoMatch,
aRule,
aIdOrNameOrAcc
) {
// eslint-disable-next-line mozilla/no-compare-against-boolean-literals
var expectMove = aIdOrNameOrAcc != false;
this.invoke = function virtualCursorChangedInvoker_invoke() {
VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
var moved = aDocAcc.virtualCursor.moveToPoint(
aRule,
aX,
aY,
aIgnoreNoMatch
);
SimpleTest.ok(
(expectMove && moved) || (!expectMove && !moved),
"moved pivot"
);
};
this.getID = function setVCPosInvoker_getID() {
return (
"Do " + (expectMove ? "" : "no-op ") + "moveToPoint " + aIdOrNameOrAcc
);
};
if (expectMove) {
this.eventSeq = [
new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, "moveToPoint", true),
];
} else {
this.eventSeq = [];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
];
}
}
/**
* Change the pivot modalRoot
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aModalRootAcc [in] accessible of the modal root, or null
* @param aExpectedResult [in] error result expected. 0 if expecting success
*/
function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult) {
this.invoke = function setModalRootInvoker_invoke() {
var errorResult = 0;
try {
aDocAcc.virtualCursor.modalRoot = aModalRootAcc;
} catch (x) {
SimpleTest.ok(
x.result,
"Unexpected exception when changing modal root: " + x
);
errorResult = x.result;
}
SimpleTest.is(
errorResult,
aExpectedResult,
"Did not get expected result when changing modalRoot"
);
};
this.getID = function setModalRootInvoker_getID() {
return "Set modalRoot to " + prettyName(aModalRootAcc);
};
this.eventSeq = [];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc),
];
}
/**
* Add invokers to a queue to test a rule and an expected sequence of element ids
* or accessible names for that rule in the given document.
*
* @param aQueue [in] event queue in which to push invoker sequence.
* @param aDocAcc [in] the managing document of the virtual cursor we are
* testing
* @param aRule [in] the traversal rule to use in the invokers
* @param aModalRoot [in] a modal root to use in this traversal sequence
* @param aSequence [in] a sequence of accessible names or element ids to expect
* with the given rule in the given document
*/
function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence) {
aDocAcc.virtualCursor.position = null;
// Add modal root (if any)
aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
for (let i = 1; i < aSequence.length; i++) {
let invoker = new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
aQueue.push(invoker);
}
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
for (let i = aSequence.length - 2; i >= 0; i--) {
let invoker = new setVCPosInvoker(
aDocAcc,
"movePrevious",
aRule,
aSequence[i]
);
aQueue.push(invoker);
}
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
aQueue.push(
new setVCPosInvoker(
aDocAcc,
"moveLast",
aRule,
aSequence[aSequence.length - 1]
)
);
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
// set isFromUserInput to false, just to test..
aQueue.push(
new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0], false)
);
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
// Remove modal root (if any).
aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
}
/**
* A checker for removing an accessible while the virtual cursor is on it.
*/
function removeVCPositionChecker(aDocAcc, aHiddenParentAcc) {
this.__proto__ = new invokerChecker(EVENT_REORDER, aHiddenParentAcc);
this.check = function removeVCPositionChecker_check(aEvent) {
var errorResult = 0;
try {
aDocAcc.virtualCursor.moveNext(ObjectTraversalRule);
} catch (x) {
errorResult = x.result;
}
SimpleTest.is(
errorResult,
NS_ERROR_NOT_IN_TREE,
"Expecting NOT_IN_TREE error when moving pivot from invalid position."
);
};
}
/**
* Put the virtual cursor's position on an object, and then remove it.
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aPosNode [in] DOM node to hide after virtual cursor's position is
* set to it.
*/
function removeVCPositionInvoker(aDocAcc, aPosNode) {
this.accessible = getAccessible(aPosNode);
this.invoke = function removeVCPositionInvoker_invoke() {
aDocAcc.virtualCursor.position = this.accessible;
aPosNode.remove();
};
this.getID = function removeVCPositionInvoker_getID() {
return "Bring virtual cursor to accessible, and remove its DOM node.";
};
this.eventSeq = [
new removeVCPositionChecker(aDocAcc, this.accessible.parent),
];
}
/**
* A checker for removing the pivot root and then calling moveFirst, and
* checking that an exception is thrown.
*/
function removeVCRootChecker(aPivot) {
this.__proto__ = new invokerChecker(EVENT_REORDER, aPivot.root.parent);
this.check = function removeVCRootChecker_check(aEvent) {
var errorResult = 0;
try {
aPivot.moveLast(ObjectTraversalRule);
} catch (x) {
errorResult = x.result;
}
SimpleTest.is(
errorResult,
NS_ERROR_NOT_IN_TREE,
"Expecting NOT_IN_TREE error when moving pivot from invalid position."
);
};
}
/**
* Create a pivot, remove its root, and perform an operation where the root is
* needed.
*
* @param aRootNode [in] DOM node of which accessible will be the root of the
* pivot. Should have more than one child.
*/
function removeVCRootInvoker(aRootNode) {
this.pivot = gAccService.createAccessiblePivot(getAccessible(aRootNode));
this.invoke = function removeVCRootInvoker_invoke() {
this.pivot.position = this.pivot.root.firstChild;
aRootNode.remove();
};
this.getID = function removeVCRootInvoker_getID() {
return "Remove root of pivot from tree.";
};
this.eventSeq = [new removeVCRootChecker(this.pivot)];
}
/**
* A debug utility for writing proper sequences for queueTraversalSequence.
*/
function dumpTraversalSequence(aPivot, aRule) {
var sequence = [];
if (aPivot.moveFirst(aRule)) {
do {
sequence.push("'" + prettyName(aPivot.position) + "'");
} while (aPivot.moveNext(aRule));
}
SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
}

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
doc_virtualcursor.html
!/accessible/tests/mochitest/*.js
[test_virtualcursor.html]

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>Pivot test document</title>
<meta charset="utf-8" />
</head>
<body>
<h1 id="heading-1-1">Main Title</h1>
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
<p id="paragraph-1">
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
leo, id <a href="#">semper</a> nulla.
</p>
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
<p id="paragraph-2" aria-hidden="">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<p id="paragraph-3" aria-hidden="true">
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
You know, the <a href="#">singer-songwriter</a>.
</p>
<p style="opacity: 0;" id="paragraph-4">
This is completely transparent
</p>
<iframe
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
</iframe>
<div id="hide-me">Hide me</div>
<p id="links" aria-hidden="false">
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
</p>
<ul>
<li>Hello<span> </span></li>
<li>World</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html>
<head>
<title>Tests pivot functionality in virtual cursors</title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
</script>
<script src="chrome://mochikit/content/chrome-harness.js">
</script>
<script type="application/javascript" src="../common.js"></script>
<script type="application/javascript" src="../browser.js"></script>
<script type="application/javascript" src="../events.js"></script>
<script type="application/javascript" src="../role.js"></script>
<script type="application/javascript" src="../states.js"></script>
<script type="application/javascript" src="../pivot.js"></script>
<script type="application/javascript" src="../layout.js"></script>
<script type="application/javascript">
var gBrowserWnd = null;
var gQueue = null;
function doTest() {
var rootAcc = getAccessible(browserDocument(), [nsIAccessibleDocument]);
ok(rootAcc.virtualCursor,
"root document does not have virtualCursor");
var doc = currentTabDocument();
var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
// Test that embedded documents have their own virtual cursor.
is(docAcc.childDocumentCount, 1, "Expecting one child document");
ok(docAcc.getChildDocumentAt(0).virtualCursor,
"child document does not have virtualCursor");
gQueue = new eventQueue();
gQueue.onFinish = function onFinish() {
closeBrowserWindow();
};
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
["heading-1-1", "heading-2-2"]);
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule, null,
["Main Title", "Lorem ipsum ",
"dolor", " sit amet. Integer vitae urna leo, id ",
"semper", " nulla. ", "Second Section Title",
"Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
"An ", "embedded", " document.", "Hide me", "Link 1", "Link 2",
"Link 3", "Hello", "World"]);
gQueue.push(new removeVCPositionInvoker(
docAcc, doc.getElementById("hide-me")));
gQueue.push(new removeVCRootInvoker(
doc.getElementById("links")));
var [x, y] = getBounds(getAccessible(doc.getElementById("heading-1-1")));
gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
HeadersTraversalRule, "heading-1-1"));
// Already on the point, so we should not get a move event.
gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
HeadersTraversalRule, false));
// Attempting a coordinate outside any header, should not move.
gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, true,
HeadersTraversalRule, false));
// Attempting a coordinate outside any header, should move to null
gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false,
HeadersTraversalRule, null));
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule,
getAccessible(doc.getElementById("paragraph-1")),
["Lorem ipsum ", "dolor", " sit amet. Integer vitae urna leo, id ",
"semper", " nulla. "]);
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
NS_ERROR_INVALID_ARG));
gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "dolor"));
gQueue.invoke();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
/* We open a new browser because we need to test with a top-level content
document. */
openBrowserWindow(
doTest,
getRootDirectory(window.location.href) + "doc_virtualcursor.html");
});
</script>
</head>
<body id="body">
<a target="_blank"
title="Introduce virtual cursor/soft focus functionality to a11y API"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -14,7 +14,6 @@ UNIFIED_SOURCES += [
"xpcAccessibleHyperLink.cpp",
"xpcAccessibleHyperText.cpp",
"xpcAccessibleImage.cpp",
"xpcAccessiblePivot.cpp",
"xpcAccessibleSelectable.cpp",
"xpcAccessibleTable.cpp",
"xpcAccessibleTableCell.cpp",

View File

@ -6,7 +6,7 @@
#include "mozilla/dom/Document.h"
#include "xpcAccessiblePivot.h"
#include "nsAccessiblePivot.h"
#include "nsAccessibilityService.h"
#include "Platform.h"
#include "xpcAccessibleApplication.h"
@ -236,7 +236,10 @@ xpcAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
NS_ENSURE_ARG(aRoot);
*aPivot = nullptr;
xpcAccessiblePivot* pivot = new xpcAccessiblePivot(aRoot);
LocalAccessible* accessibleRoot = aRoot->ToInternalAccessible();
NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
NS_ADDREF(*aPivot = pivot);
return NS_OK;

View File

@ -132,6 +132,17 @@ xpcAccessibleDocument::GetChildDocumentAt(uint32_t aIndex,
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

View File

@ -46,6 +46,7 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) final;
NS_IMETHOD GetChildDocumentAt(uint32_t aIndex,
nsIAccessibleDocument** aDocument) final;
NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) final;
/**
* Return XPCOM wrapper for the internal accessible.

View File

@ -1,152 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "xpcAccessiblePivot.h"
#include "Pivot.h"
using namespace mozilla::a11y;
/**
* An object that stores a given traversal rule during the pivot movement.
*/
class xpcPivotRule : public PivotRule {
public:
explicit xpcPivotRule(nsIAccessibleTraversalRule* aRule) : mRule(aRule) {}
~xpcPivotRule() {}
virtual uint16_t Match(Accessible* aAcc) override;
private:
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
};
////////////////////////////////////////////////////////////////////////////////
// xpcAccessiblePivot
xpcAccessiblePivot::xpcAccessiblePivot(nsIAccessible* aRoot) : mRoot(aRoot) {
NS_ASSERTION(aRoot, "A root accessible is required");
}
xpcAccessiblePivot::~xpcAccessiblePivot() {}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_CYCLE_COLLECTION(xpcAccessiblePivot, mRoot)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(xpcAccessiblePivot)
NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessiblePivot)
////////////////////////////////////////////////////////////////////////////////
// nsIAccessiblePivot
NS_IMETHODIMP
xpcAccessiblePivot::Next(nsIAccessible* aAnchor,
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
uint8_t aArgc, nsIAccessible** aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
Accessible* root = Root();
Accessible* anchor = aAnchor->ToInternalGeneric();
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
Pivot pivot(Root());
xpcPivotRule rule(aRule);
Accessible* result =
pivot.Next(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
NS_IF_ADDREF(*aResult = ToXPC(result));
return NS_OK;
}
NS_IMETHODIMP
xpcAccessiblePivot::Prev(nsIAccessible* aAnchor,
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
uint8_t aArgc, nsIAccessible** aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
Accessible* root = Root();
Accessible* anchor = aAnchor->ToInternalGeneric();
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
Pivot pivot(Root());
xpcPivotRule rule(aRule);
Accessible* result =
pivot.Prev(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
NS_IF_ADDREF(*aResult = ToXPC(result));
return NS_OK;
}
NS_IMETHODIMP
xpcAccessiblePivot::First(nsIAccessibleTraversalRule* aRule,
nsIAccessible** aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
Accessible* root = Root();
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
Pivot pivot(root);
xpcPivotRule rule(aRule);
Accessible* result = pivot.First(rule);
NS_IF_ADDREF(*aResult = ToXPC(result));
return NS_OK;
}
NS_IMETHODIMP
xpcAccessiblePivot::Last(nsIAccessibleTraversalRule* aRule,
nsIAccessible** aResult) {
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
Accessible* root = Root();
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
Pivot pivot(root);
xpcPivotRule rule(aRule);
Accessible* result = pivot.Last(rule);
NS_IF_ADDREF(*aResult = ToXPC(result));
return NS_OK;
}
NS_IMETHODIMP
xpcAccessiblePivot::AtPoint(int32_t aX, int32_t aY,
nsIAccessibleTraversalRule* aRule,
nsIAccessible** aResult) {
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aRule);
Accessible* root = Root();
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
xpcPivotRule rule(aRule);
Pivot pivot(root);
Accessible* result = pivot.AtPoint(aX, aY, rule);
NS_IF_ADDREF(*aResult = ToXPC(result));
return NS_OK;
}
uint16_t xpcPivotRule::Match(Accessible* aAcc) {
uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return matchResult;
}

View File

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _xpcAccessiblePivot_H_
#define _xpcAccessiblePivot_H_
#include "nsIAccessiblePivot.h"
#include "Accessible.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
/**
* Class represents an accessible pivot.
*/
class xpcAccessiblePivot final : public nsIAccessiblePivot {
public:
explicit xpcAccessiblePivot(nsIAccessible* aRoot);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessiblePivot,
nsIAccessiblePivot)
NS_DECL_NSIACCESSIBLEPIVOT
private:
~xpcAccessiblePivot();
xpcAccessiblePivot() = delete;
xpcAccessiblePivot(const xpcAccessiblePivot&) = delete;
void operator=(const xpcAccessiblePivot&) = delete;
Accessible* Root() { return mRoot ? mRoot->ToInternalGeneric() : nullptr; }
/*
* The root accessible.
*/
RefPtr<nsIAccessible> mRoot;
};
#endif