mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Backed out changeset 8be4693aecbb (bug 1754905) for causing bustages in xpcAccessiblePivot.h. CLOSED TREE
This commit is contained in:
parent
3f23020c5c
commit
f0967c3305
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -43,6 +43,7 @@ UNIFIED_SOURCES += [
|
||||
"FocusManager.cpp",
|
||||
"NotificationController.cpp",
|
||||
"nsAccessibilityService.cpp",
|
||||
"nsAccessiblePivot.cpp",
|
||||
"nsAccUtils.cpp",
|
||||
"nsCoreUtils.cpp",
|
||||
"nsEventShell.cpp",
|
||||
|
395
accessible/base/nsAccessiblePivot.cpp
Normal file
395
accessible/base/nsAccessiblePivot.cpp
Normal 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;
|
||||
}
|
124
accessible/base/nsAccessiblePivot.h
Normal file
124
accessible/base/nsAccessiblePivot.h
Normal 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
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "HyperTextAccessible-inl.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccessiblePivot.h"
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "AccAttributes.h"
|
||||
#include "DocAccessible.h"
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
||||
|
66
accessible/tests/browser/e10s/browser_events_vcchange.js
Normal file
66
accessible/tests/browser/e10s/browser_events_vcchange.js
Normal 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 }
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
[DEFAULT]
|
||||
subsuite = a11y
|
||||
support-files =
|
||||
!/accessible/tests/browser/shared-head.js
|
||||
head.js
|
||||
!/accessible/tests/mochitest/*.js
|
||||
prefs =
|
||||
javascript.options.asyncstack_capture_debuggee_only=false
|
||||
|
||||
[browser_pivot.js]
|
@ -1,103 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests Pivot API
|
||||
*/
|
||||
addAccessibleTask(
|
||||
`
|
||||
<h1 id="heading-1-1">Main Title</h1>
|
||||
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
|
||||
<p id="paragraph-1">
|
||||
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
|
||||
leo, id <a href="#">semper</a> nulla.
|
||||
</p>
|
||||
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
|
||||
<p id="paragraph-2" aria-hidden="">
|
||||
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
|
||||
<p id="paragraph-3" aria-hidden="true">
|
||||
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
|
||||
You know, the <a href="#">singer-songwriter</a>.
|
||||
</p>
|
||||
<p style="opacity: 0;" id="paragraph-4">
|
||||
This is completely transparent
|
||||
</p>
|
||||
<iframe
|
||||
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
|
||||
</iframe>
|
||||
<div id="hide-me">Hide me</div>
|
||||
<p id="links" aria-hidden="false">
|
||||
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
|
||||
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
|
||||
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>
|
||||
</p>
|
||||
<ul>
|
||||
<li>Hello<span> </span></li>
|
||||
<li>World</li>
|
||||
</ul>
|
||||
`,
|
||||
async function (browser, docAcc) {
|
||||
let pivot = gAccService.createAccessiblePivot(docAcc);
|
||||
testPivotSequence(pivot, HeadersTraversalRule, [
|
||||
"heading-1-1",
|
||||
"heading-2-2",
|
||||
]);
|
||||
|
||||
testPivotSequence(pivot, ObjectTraversalRule, [
|
||||
"Main Title",
|
||||
"Lorem ipsum ",
|
||||
"dolor",
|
||||
" sit amet. Integer vitae urna leo, id ",
|
||||
"semper",
|
||||
" nulla. ",
|
||||
"Second Section Title",
|
||||
"Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.",
|
||||
"An ",
|
||||
"embedded",
|
||||
" document.",
|
||||
"Hide me",
|
||||
"Link 1",
|
||||
"Link 2",
|
||||
"Link 3",
|
||||
"Hello",
|
||||
"World",
|
||||
]);
|
||||
|
||||
let hideMeAcc = findAccessibleChildByID(docAcc, "hide-me");
|
||||
let onHide = waitForEvent(EVENT_HIDE, hideMeAcc);
|
||||
invokeContentTask(browser, [], () => {
|
||||
content.document.getElementById("hide-me").remove();
|
||||
});
|
||||
|
||||
await onHide;
|
||||
testFailsWithNotInTree(
|
||||
() => pivot.next(hideMeAcc, ObjectTraversalRule),
|
||||
"moveNext from defunct accessible should fail"
|
||||
);
|
||||
|
||||
let linksAcc = findAccessibleChildByID(docAcc, "links");
|
||||
|
||||
let removedRootPivot = gAccService.createAccessiblePivot(linksAcc);
|
||||
onHide = waitForEvent(EVENT_HIDE, linksAcc);
|
||||
invokeContentTask(browser, [], () => {
|
||||
content.document.getElementById("links").remove();
|
||||
});
|
||||
|
||||
await onHide;
|
||||
testFailsWithNotInTree(
|
||||
() => removedRootPivot.last(ObjectTraversalRule),
|
||||
"moveLast with pivot with defunct root should fail"
|
||||
);
|
||||
|
||||
let [x, y] = getBounds(findAccessibleChildByID(docAcc, "heading-1-1"));
|
||||
let hitacc = pivot.atPoint(x + 1, y + 1, HeadersTraversalRule);
|
||||
is(getIdOrName(hitacc), "heading-1-1", "Matching accessible at point");
|
||||
|
||||
hitacc = pivot.atPoint(x - 1, y - 1, HeadersTraversalRule);
|
||||
ok(!hitacc, "No heading at given point");
|
||||
},
|
||||
{ iframe: true, remoteIframe: true, topLevel: true, chrome: true }
|
||||
);
|
@ -1,122 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* exported HeadersTraversalRule, ObjectTraversalRule, testPivotSequence, testFailsWithNotInTree */
|
||||
|
||||
// Load the shared-head file first.
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
|
||||
this
|
||||
);
|
||||
|
||||
/* import-globals-from ../../mochitest/layout.js */
|
||||
/* import-globals-from ../../mochitest/role.js */
|
||||
/* import-globals-from ../../mochitest/states.js */
|
||||
loadScripts(
|
||||
{ name: "common.js", dir: MOCHITESTS_DIR },
|
||||
{ name: "promisified-events.js", dir: MOCHITESTS_DIR },
|
||||
{ name: "states.js", dir: MOCHITESTS_DIR },
|
||||
{ name: "role.js", dir: MOCHITESTS_DIR },
|
||||
{ name: "layout.js", dir: MOCHITESTS_DIR }
|
||||
);
|
||||
|
||||
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||
|
||||
const NS_ERROR_NOT_IN_TREE = 0x80780026;
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
// Traversal rules
|
||||
|
||||
/**
|
||||
* Rule object to traverse all focusable nodes and text nodes.
|
||||
*/
|
||||
const HeadersTraversalRule = {
|
||||
match(acc) {
|
||||
return acc.role == ROLE_HEADING ? FILTER_MATCH : FILTER_IGNORE;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
||||
};
|
||||
|
||||
/**
|
||||
* Traversal rule for all focusable nodes or leafs.
|
||||
*/
|
||||
const ObjectTraversalRule = {
|
||||
match(acc) {
|
||||
let [state, extstate] = getStates(acc);
|
||||
if (state & STATE_INVISIBLE) {
|
||||
return FILTER_IGNORE;
|
||||
}
|
||||
|
||||
if ((extstate & EXT_STATE_OPAQUE) == 0) {
|
||||
return FILTER_IGNORE | FILTER_IGNORE_SUBTREE;
|
||||
}
|
||||
|
||||
let rv = FILTER_IGNORE;
|
||||
let role = acc.role;
|
||||
if (
|
||||
hasState(acc, STATE_FOCUSABLE) &&
|
||||
role != ROLE_DOCUMENT &&
|
||||
role != ROLE_INTERNAL_FRAME
|
||||
) {
|
||||
rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
|
||||
} else if (
|
||||
acc.childCount == 0 &&
|
||||
role != ROLE_LISTITEM_MARKER &&
|
||||
acc.name.trim()
|
||||
) {
|
||||
rv = FILTER_MATCH;
|
||||
}
|
||||
|
||||
return rv;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([nsIAccessibleTraversalRule]),
|
||||
};
|
||||
|
||||
function getIdOrName(acc) {
|
||||
let id = getAccessibleDOMNodeID(acc);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
return acc.name;
|
||||
}
|
||||
|
||||
function* pivotNextGenerator(pivot, rule) {
|
||||
for (let acc = pivot.first(rule); acc; acc = pivot.next(acc, rule)) {
|
||||
yield acc;
|
||||
}
|
||||
}
|
||||
|
||||
function* pivotPreviousGenerator(pivot, rule) {
|
||||
for (let acc = pivot.last(rule); acc; acc = pivot.prev(acc, rule)) {
|
||||
yield acc;
|
||||
}
|
||||
}
|
||||
|
||||
function testPivotSequence(pivot, rule, expectedSequence) {
|
||||
is(
|
||||
JSON.stringify([...pivotNextGenerator(pivot, rule)].map(getIdOrName)),
|
||||
JSON.stringify(expectedSequence),
|
||||
"Forward pivot sequence is correct"
|
||||
);
|
||||
is(
|
||||
JSON.stringify([...pivotPreviousGenerator(pivot, rule)].map(getIdOrName)),
|
||||
JSON.stringify([...expectedSequence].reverse()),
|
||||
"Reverse pivot sequence is correct"
|
||||
);
|
||||
}
|
||||
|
||||
function testFailsWithNotInTree(func, msg) {
|
||||
try {
|
||||
func();
|
||||
ok(false, msg);
|
||||
} catch (x) {
|
||||
is(x.result, NS_ERROR_NOT_IN_TREE, `Expecting NOT_IN_TREE: ${msg}`);
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ A11Y_MANIFESTS += [
|
||||
"hyperlink/a11y.ini",
|
||||
"hypertext/a11y.ini",
|
||||
"name/a11y.ini",
|
||||
"pivot/a11y.ini",
|
||||
"relations/a11y.ini",
|
||||
"role/a11y.ini",
|
||||
"scroll/a11y.ini",
|
||||
|
575
accessible/tests/mochitest/pivot.js
Normal file
575
accessible/tests/mochitest/pivot.js
Normal 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");
|
||||
}
|
6
accessible/tests/mochitest/pivot/a11y.ini
Normal file
6
accessible/tests/mochitest/pivot/a11y.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
doc_virtualcursor.html
|
||||
!/accessible/tests/mochitest/*.js
|
||||
|
||||
[test_virtualcursor.html]
|
38
accessible/tests/mochitest/pivot/doc_virtualcursor.html
Normal file
38
accessible/tests/mochitest/pivot/doc_virtualcursor.html
Normal 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>
|
112
accessible/tests/mochitest/pivot/test_virtualcursor.html
Normal file
112
accessible/tests/mochitest/pivot/test_virtualcursor.html
Normal 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>
|
@ -14,7 +14,6 @@ UNIFIED_SOURCES += [
|
||||
"xpcAccessibleHyperLink.cpp",
|
||||
"xpcAccessibleHyperText.cpp",
|
||||
"xpcAccessibleImage.cpp",
|
||||
"xpcAccessiblePivot.cpp",
|
||||
"xpcAccessibleSelectable.cpp",
|
||||
"xpcAccessibleTable.cpp",
|
||||
"xpcAccessibleTableCell.cpp",
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -1,152 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "xpcAccessiblePivot.h"
|
||||
|
||||
#include "Pivot.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
/**
|
||||
* An object that stores a given traversal rule during the pivot movement.
|
||||
*/
|
||||
class xpcPivotRule : public PivotRule {
|
||||
public:
|
||||
explicit xpcPivotRule(nsIAccessibleTraversalRule* aRule) : mRule(aRule) {}
|
||||
~xpcPivotRule() {}
|
||||
|
||||
virtual uint16_t Match(Accessible* aAcc) override;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// xpcAccessiblePivot
|
||||
|
||||
xpcAccessiblePivot::xpcAccessiblePivot(nsIAccessible* aRoot) : mRoot(aRoot) {
|
||||
NS_ASSERTION(aRoot, "A root accessible is required");
|
||||
}
|
||||
|
||||
xpcAccessiblePivot::~xpcAccessiblePivot() {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(xpcAccessiblePivot, mRoot)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessiblePivot)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(xpcAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessiblePivot)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIAccessiblePivot
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessiblePivot::Next(nsIAccessible* aAnchor,
|
||||
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
|
||||
uint8_t aArgc, nsIAccessible** aResult) {
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
Accessible* root = Root();
|
||||
Accessible* anchor = aAnchor->ToInternalGeneric();
|
||||
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
|
||||
|
||||
Pivot pivot(Root());
|
||||
xpcPivotRule rule(aRule);
|
||||
Accessible* result =
|
||||
pivot.Next(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
|
||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessiblePivot::Prev(nsIAccessible* aAnchor,
|
||||
nsIAccessibleTraversalRule* aRule, bool aIncludeStart,
|
||||
uint8_t aArgc, nsIAccessible** aResult) {
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
Accessible* root = Root();
|
||||
Accessible* anchor = aAnchor->ToInternalGeneric();
|
||||
NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE);
|
||||
|
||||
Pivot pivot(Root());
|
||||
xpcPivotRule rule(aRule);
|
||||
Accessible* result =
|
||||
pivot.Prev(anchor, rule, (aArgc > 0) ? aIncludeStart : false);
|
||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessiblePivot::First(nsIAccessibleTraversalRule* aRule,
|
||||
nsIAccessible** aResult) {
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
Accessible* root = Root();
|
||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
||||
|
||||
Pivot pivot(root);
|
||||
xpcPivotRule rule(aRule);
|
||||
Accessible* result = pivot.First(rule);
|
||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessiblePivot::Last(nsIAccessibleTraversalRule* aRule,
|
||||
nsIAccessible** aResult) {
|
||||
NS_ENSURE_ARG(aResult);
|
||||
NS_ENSURE_ARG(aRule);
|
||||
|
||||
Accessible* root = Root();
|
||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
||||
|
||||
Pivot pivot(root);
|
||||
xpcPivotRule rule(aRule);
|
||||
Accessible* result = pivot.Last(rule);
|
||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessiblePivot::AtPoint(int32_t aX, int32_t aY,
|
||||
nsIAccessibleTraversalRule* aRule,
|
||||
nsIAccessible** aResult) {
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
NS_ENSURE_ARG_POINTER(aRule);
|
||||
|
||||
Accessible* root = Root();
|
||||
NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE);
|
||||
|
||||
xpcPivotRule rule(aRule);
|
||||
Pivot pivot(root);
|
||||
|
||||
Accessible* result = pivot.AtPoint(aX, aY, rule);
|
||||
NS_IF_ADDREF(*aResult = ToXPC(result));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint16_t xpcPivotRule::Match(Accessible* aAcc) {
|
||||
uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
|
||||
|
||||
DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
return matchResult;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _xpcAccessiblePivot_H_
|
||||
#define _xpcAccessiblePivot_H_
|
||||
|
||||
#include "nsIAccessiblePivot.h"
|
||||
|
||||
#include "Accessible.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
/**
|
||||
* Class represents an accessible pivot.
|
||||
*/
|
||||
class xpcAccessiblePivot final : public nsIAccessiblePivot {
|
||||
public:
|
||||
explicit xpcAccessiblePivot(nsIAccessible* aRoot);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessiblePivot,
|
||||
nsIAccessiblePivot)
|
||||
|
||||
NS_DECL_NSIACCESSIBLEPIVOT
|
||||
|
||||
private:
|
||||
~xpcAccessiblePivot();
|
||||
xpcAccessiblePivot() = delete;
|
||||
xpcAccessiblePivot(const xpcAccessiblePivot&) = delete;
|
||||
void operator=(const xpcAccessiblePivot&) = delete;
|
||||
|
||||
Accessible* Root() { return mRoot ? mRoot->ToInternalGeneric() : nullptr; }
|
||||
|
||||
/*
|
||||
* The root accessible.
|
||||
*/
|
||||
RefPtr<nsIAccessible> mRoot;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user