From f0967c33054b3d7c5f9b5e4cf4ae1a85d9ded553 Mon Sep 17 00:00:00 2001 From: Stanca Serban Date: Sat, 24 Jun 2023 03:22:31 +0300 Subject: [PATCH] Backed out changeset 8be4693aecbb (bug 1754905) for causing bustages in xpcAccessiblePivot.h. CLOSED TREE --- accessible/android/AccessibleWrap.cpp | 8 +- accessible/android/Platform.cpp | 7 +- accessible/base/Pivot.cpp | 1 - accessible/base/moz.build | 1 + accessible/base/nsAccessiblePivot.cpp | 395 ++++++++++++++ accessible/base/nsAccessiblePivot.h | 124 +++++ accessible/basetypes/Accessible.cpp | 6 +- accessible/generic/DocAccessible-inl.h | 9 + accessible/generic/DocAccessible.cpp | 27 + accessible/generic/DocAccessible.h | 19 +- accessible/generic/HyperTextAccessible.cpp | 1 + accessible/generic/LocalAccessible.cpp | 1 + accessible/interfaces/nsIAccessibleDocument.idl | 5 + accessible/interfaces/nsIAccessiblePivot.idl | 126 ++++- accessible/mac/mozTableAccessible.mm | 1 - accessible/moz.build | 1 - accessible/tests/browser/e10s/browser.ini | 1 + .../tests/browser/e10s/browser_events_vcchange.js | 66 +++ accessible/tests/browser/pivot/browser.ini | 10 - accessible/tests/browser/pivot/browser_pivot.js | 103 ---- accessible/tests/browser/pivot/head.js | 122 ----- accessible/tests/mochitest/moz.build | 1 + accessible/tests/mochitest/pivot.js | 575 +++++++++++++++++++++ accessible/tests/mochitest/pivot/a11y.ini | 6 + .../tests/mochitest/pivot/doc_virtualcursor.html | 38 ++ .../tests/mochitest/pivot/test_virtualcursor.html | 112 ++++ accessible/xpcom/moz.build | 1 - accessible/xpcom/xpcAccessibilityService.cpp | 7 +- accessible/xpcom/xpcAccessibleDocument.cpp | 11 + accessible/xpcom/xpcAccessibleDocument.h | 1 + accessible/xpcom/xpcAccessiblePivot.cpp | 152 ------ accessible/xpcom/xpcAccessiblePivot.h | 43 -- 32 files changed, 1517 insertions(+), 464 deletions(-) create mode 100644 accessible/base/nsAccessiblePivot.cpp create mode 100644 accessible/base/nsAccessiblePivot.h create mode 100644 accessible/tests/browser/e10s/browser_events_vcchange.js delete mode 100644 accessible/tests/browser/pivot/browser.ini delete mode 100644 accessible/tests/browser/pivot/browser_pivot.js delete mode 100644 accessible/tests/browser/pivot/head.js create mode 100644 accessible/tests/mochitest/pivot.js create mode 100644 accessible/tests/mochitest/pivot/a11y.ini create mode 100644 accessible/tests/mochitest/pivot/doc_virtualcursor.html create mode 100644 accessible/tests/mochitest/pivot/test_virtualcursor.html delete mode 100644 accessible/xpcom/xpcAccessiblePivot.cpp delete mode 100644 accessible/xpcom/xpcAccessiblePivot.h diff --git a/accessible/android/AccessibleWrap.cpp b/accessible/android/AccessibleWrap.cpp index 2f678ad3e029..20543ed470ad 100644 --- a/accessible/android/AccessibleWrap.cpp +++ b/accessible/android/AccessibleWrap.cpp @@ -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 newPosition = static_cast(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; } diff --git a/accessible/android/Platform.cpp b/accessible/android/Platform.cpp index d314e7b8e501..c90b53e59846 100644 --- a/accessible/android/Platform.cpp +++ b/accessible/android/Platform.cpp @@ -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, diff --git a/accessible/base/Pivot.cpp b/accessible/base/Pivot.cpp index 686f1a6371e8..a3a177e62b01 100644 --- a/accessible/base/Pivot.cpp +++ b/accessible/base/Pivot.cpp @@ -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" diff --git a/accessible/base/moz.build b/accessible/base/moz.build index a31f3198b289..4a8e19b2799d 100644 --- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -43,6 +43,7 @@ UNIFIED_SOURCES += [ "FocusManager.cpp", "NotificationController.cpp", "nsAccessibilityService.cpp", + "nsAccessiblePivot.cpp", "nsAccUtils.cpp", "nsCoreUtils.cpp", "nsEventShell.cpp", diff --git a/accessible/base/nsAccessiblePivot.cpp b/accessible/base/nsAccessiblePivot.cpp new file mode 100644 index 000000000000..cc46d7d7d76b --- /dev/null +++ b/accessible/base/nsAccessiblePivot.cpp @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAccessiblePivot.h" + +#include "HyperTextAccessible.h" +#include "nsAccUtils.h" +#include "States.h" +#include "Pivot.h" +#include "xpcAccessibleDocument.h" +#include "nsTArray.h" +#include "mozilla/Maybe.h" + +using namespace mozilla::a11y; +using mozilla::DebugOnly; +using mozilla::Maybe; + +/** + * An object that stores a given traversal rule during the pivot movement. + */ +class RuleCache : public PivotRule { + public: + explicit RuleCache(nsIAccessibleTraversalRule* aRule) + : mRule(aRule), mPreFilter{0} {} + ~RuleCache() {} + + virtual uint16_t Match(Accessible* aAcc) override; + + private: + nsCOMPtr mRule; + Maybe> 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 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 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 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 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 rv = mRule->Match(ToXPC(aAcc), &matchResult); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + return result | matchResult; +} diff --git a/accessible/base/nsAccessiblePivot.h b/accessible/base/nsAccessiblePivot.h new file mode 100644 index 000000000000..4b781d132118 --- /dev/null +++ b/accessible/base/nsAccessiblePivot.h @@ -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 mRoot; + + /* + * The temporary modal root accessible. + */ + RefPtr mModalRoot; + + /* + * The current pivot position. + */ + RefPtr mPosition; + + /* + * The list of pivot-changed observers. + */ + nsTObserverArray > mObservers; +}; + +#endif diff --git a/accessible/basetypes/Accessible.cpp b/accessible/basetypes/Accessible.cpp index 033ade6c3bfc..cc41cd3a4624 100644 --- a/accessible/basetypes/Accessible.cpp +++ b/accessible/basetypes/Accessible.cpp @@ -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 diff --git a/accessible/generic/DocAccessible-inl.h b/accessible/generic/DocAccessible-inl.h index 0b7b43012057..493ca1fd2126 100644 --- a/accessible/generic/DocAccessible-inl.h +++ b/accessible/generic/DocAccessible-inl.h @@ -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' diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 2eb75437b959..4e6df7fdee0d 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -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(); @@ -714,6 +724,23 @@ std::pair DocAccessible::ComputeScrollData( } //////////////////////////////////////////////////////////////////////////////// +// nsIAccessiblePivotObserver + +NS_IMETHODIMP +DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot, + nsIAccessible* aOldAccessible, + nsIAccessible* aNewAccessible, + PivotMoveReason aReason, bool aIsFromUserInput) { + RefPtr event = new AccVCChangeEvent( + this, (aOldAccessible ? aOldAccessible->ToInternalAccessible() : nullptr), + (aNewAccessible ? aNewAccessible->ToInternalAccessible() : nullptr), + aReason, aIsFromUserInput ? eFromUserInput : eNoUserInput); + nsEventShell::FireEvent(event); + + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// // nsIDocumentObserver NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible) diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index c05c44dff72d..d87cecfda70b 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -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; @@ -129,6 +136,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. */ bool HasShutdown() const { return !mPresShell; } @@ -695,6 +707,11 @@ class DocAccessible : public HyperTextAccessibleWrap, nsTArray> mChildDocuments; /** + * The virtual cursor of the document. + */ + RefPtr mVirtualCursor; + + /** * A storage class for pairing content with one of its relation attributes. */ class AttrRelProvider { diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 9dfe8b395d8e..817324f01d38 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -7,6 +7,7 @@ #include "HyperTextAccessible-inl.h" #include "nsAccessibilityService.h" +#include "nsAccessiblePivot.h" #include "nsIAccessibleTypes.h" #include "AccAttributes.h" #include "DocAccessible.h" diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp index 056aff0448fb..dd4fd6da1e8d 100644 --- a/accessible/generic/LocalAccessible.cpp +++ b/accessible/generic/LocalAccessible.cpp @@ -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" diff --git a/accessible/interfaces/nsIAccessibleDocument.idl b/accessible/interfaces/nsIAccessibleDocument.idl index 1886621c373f..a91df31c96af 100644 --- a/accessible/interfaces/nsIAccessibleDocument.idl +++ b/accessible/interfaces/nsIAccessibleDocument.idl @@ -64,6 +64,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. */ nsIAccessibleDocument getChildDocumentAt(in unsigned long index); diff --git a/accessible/interfaces/nsIAccessiblePivot.idl b/accessible/interfaces/nsIAccessiblePivot.idl index fd27938dd2e7..59e601eb99ff 100644 --- a/accessible/interfaces/nsIAccessiblePivot.idl +++ b/accessible/interfaces/nsIAccessiblePivot.idl @@ -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; @@ -29,6 +29,21 @@ interface nsIAccessiblePivot : nsISupports 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. + */ + [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. */ - nsIAccessible atPoint(in long aX, in long aY, - in nsIAccessibleTraversalRule aRule); + 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 getMatchRoles(); + /** * Determines if a given accessible is to be accepted in our traversal rule * diff --git a/accessible/mac/mozTableAccessible.mm b/accessible/mac/mozTableAccessible.mm index 98bd5fafa876..9ba5c94f789d 100644 --- a/accessible/mac/mozTableAccessible.mm +++ b/accessible/mac/mozTableAccessible.mm @@ -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" diff --git a/accessible/moz.build b/accessible/moz.build index c12c837ba05d..f30c78f47e7f 100644 --- a/accessible/moz.build +++ b/accessible/moz.build @@ -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", diff --git a/accessible/tests/browser/e10s/browser.ini b/accessible/tests/browser/e10s/browser.ini index 761ce2980541..54b3c5692e4b 100644 --- a/accessible/tests/browser/e10s/browser.ini +++ b/accessible/tests/browser/e10s/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] diff --git a/accessible/tests/browser/e10s/browser_events_vcchange.js b/accessible/tests/browser/e10s/browser_events_vcchange.js new file mode 100644 index 000000000000..59ea62885077 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_vcchange.js @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +addAccessibleTask( + ` +

abc

+ `, + 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 } +); diff --git a/accessible/tests/browser/pivot/browser.ini b/accessible/tests/browser/pivot/browser.ini deleted file mode 100644 index 697540e4f167..000000000000 --- a/accessible/tests/browser/pivot/browser.ini +++ /dev/null @@ -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] diff --git a/accessible/tests/browser/pivot/browser_pivot.js b/accessible/tests/browser/pivot/browser_pivot.js deleted file mode 100644 index bd46ae49333d..000000000000 --- a/accessible/tests/browser/pivot/browser_pivot.js +++ /dev/null @@ -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( - ` -

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.

- -

- This is completely transparent -

- -
Hide me
- -
    -
  • Hello
  • -
  • World
  • -
- `, - 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 } -); diff --git a/accessible/tests/browser/pivot/head.js b/accessible/tests/browser/pivot/head.js deleted file mode 100644 index 8389190a6981..000000000000 --- a/accessible/tests/browser/pivot/head.js +++ /dev/null @@ -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}`); - } -} diff --git a/accessible/tests/mochitest/moz.build b/accessible/tests/mochitest/moz.build index 36e63b1ccfd2..6276f6ced945 100644 --- a/accessible/tests/mochitest/moz.build +++ b/accessible/tests/mochitest/moz.build @@ -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", diff --git a/accessible/tests/mochitest/pivot.js b/accessible/tests/mochitest/pivot.js new file mode 100644 index 000000000000..f3f853a0e1e0 --- /dev/null +++ b/accessible/tests/mochitest/pivot.js @@ -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"); +} diff --git a/accessible/tests/mochitest/pivot/a11y.ini b/accessible/tests/mochitest/pivot/a11y.ini new file mode 100644 index 000000000000..3117809fc3a4 --- /dev/null +++ b/accessible/tests/mochitest/pivot/a11y.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + doc_virtualcursor.html + !/accessible/tests/mochitest/*.js + +[test_virtualcursor.html] diff --git a/accessible/tests/mochitest/pivot/doc_virtualcursor.html b/accessible/tests/mochitest/pivot/doc_virtualcursor.html new file mode 100644 index 000000000000..a456f2dfcdea --- /dev/null +++ b/accessible/tests/mochitest/pivot/doc_virtualcursor.html @@ -0,0 +1,38 @@ + + + + Pivot test document + + + +

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.

+ +

+ This is completely transparent +

+ +
Hide me
+ +
    +
  • Hello
  • +
  • World
  • +
+ + diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor.html b/accessible/tests/mochitest/pivot/test_virtualcursor.html new file mode 100644 index 000000000000..b791e671b357 --- /dev/null +++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html @@ -0,0 +1,112 @@ + + + + Tests pivot functionality in virtual cursors + + + + + + + + + + + + + + + + + + + Mozilla Bug 698823 +

+ +
+  
+ + diff --git a/accessible/xpcom/moz.build b/accessible/xpcom/moz.build index 6385928dff66..7ba6d8ca1c1f 100644 --- a/accessible/xpcom/moz.build +++ b/accessible/xpcom/moz.build @@ -14,7 +14,6 @@ UNIFIED_SOURCES += [ "xpcAccessibleHyperLink.cpp", "xpcAccessibleHyperText.cpp", "xpcAccessibleImage.cpp", - "xpcAccessiblePivot.cpp", "xpcAccessibleSelectable.cpp", "xpcAccessibleTable.cpp", "xpcAccessibleTableCell.cpp", diff --git a/accessible/xpcom/xpcAccessibilityService.cpp b/accessible/xpcom/xpcAccessibilityService.cpp index d27a023b7495..128d51076982 100644 --- a/accessible/xpcom/xpcAccessibilityService.cpp +++ b/accessible/xpcom/xpcAccessibilityService.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; diff --git a/accessible/xpcom/xpcAccessibleDocument.cpp b/accessible/xpcom/xpcAccessibleDocument.cpp index 94b04cf2f653..2f9e191c7765 100644 --- a/accessible/xpcom/xpcAccessibleDocument.cpp +++ b/accessible/xpcom/xpcAccessibleDocument.cpp @@ -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 diff --git a/accessible/xpcom/xpcAccessibleDocument.h b/accessible/xpcom/xpcAccessibleDocument.h index e41d5d270e00..416a1c5497c9 100644 --- a/accessible/xpcom/xpcAccessibleDocument.h +++ b/accessible/xpcom/xpcAccessibleDocument.h @@ -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. diff --git a/accessible/xpcom/xpcAccessiblePivot.cpp b/accessible/xpcom/xpcAccessiblePivot.cpp deleted file mode 100644 index 8fea2d361314..000000000000 --- a/accessible/xpcom/xpcAccessiblePivot.cpp +++ /dev/null @@ -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 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 rv = mRule->Match(ToXPC(aAcc), &matchResult); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - return matchResult; -} diff --git a/accessible/xpcom/xpcAccessiblePivot.h b/accessible/xpcom/xpcAccessiblePivot.h deleted file mode 100644 index 2a14e0d23e9e..000000000000 --- a/accessible/xpcom/xpcAccessiblePivot.h +++ /dev/null @@ -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 mRoot; -}; - -#endif -- 2.11.4.GIT