Bug 698823 - Introduce virtual cursor/soft focus functionality to a11y API, r=tbsaunde, surkov, sr=neil

This commit is contained in:
Eitan Isaacson 2012-02-02 15:14:51 +09:00
Родитель 7da47ddaf3
Коммит 54a74544c9
20 изменённых файлов: 1453 добавлений и 5 удалений

Просмотреть файл

@ -62,10 +62,12 @@ XPIDLSRCS = \
nsIAccessibleProvider.idl \
nsIAccessibleSelectable.idl \
nsIAccessNode.idl \
nsIAccessibleCursorable.idl \
nsIAccessibleEvent.idl \
nsIAccessibleEditableText.idl \
nsIAccessibleHyperLink.idl \
nsIAccessibleHyperText.idl \
nsIAccessiblePivot.idl \
nsIAccessibleTable.idl \
nsIAccessibleText.idl \
nsIAccessibleValue.idl \

Просмотреть файл

@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIAccessiblePivot;
/**
* An interface implemented by an accessible object that has an associated
* virtual cursor. Typically, a top-level application or content document.
* A virtual cursor is an implementation of nsIAccessiblePivot that provides an
* exclusive spot in the cursorable's subtree, this could be used to create a
* pseudo-focus or caret browsing experience that is centered around the
* accessibility API.
*/
[scriptable, uuid(5452dea5-d235-496f-8757-3ca016ff49ff)]
interface nsIAccessibleCursorable : nsISupports
{
/**
* The virtual cursor pivot this object manages.
*/
readonly attribute nsIAccessiblePivot virtualCursor;
};

Просмотреть файл

@ -441,10 +441,15 @@ interface nsIAccessibleEvent : nsISupports
*/
const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
/**
* A cursorable's virtual cursor has changed.
*/
const unsigned long EVENT_VIRTUALCURSOR_CHANGED = 0x0056;
/**
* Help make sure event map does not get out-of-line.
*/
const unsigned long EVENT_LAST_ENTRY = 0x0056;
const unsigned long EVENT_LAST_ENTRY = 0x0057;
/**
* The type of event, based on the enumerated event values

Просмотреть файл

@ -0,0 +1,221 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
typedef short TextBoundaryType;
interface nsIAccessible;
interface nsIAccessibleText;
interface nsIAccessibleTraversalRule;
interface nsIAccessiblePivotObserver;
/**
* The pivot interface encapsulates a reference to a single place in an accessible
* subtree. The pivot is a point or a range in the accessible tree. This interface
* provides traversal methods to move the pivot to next/prev state that complies
* to a given rule.
*/
[scriptable, uuid(689058ae-e301-444f-acb0-b5c2b189f350)]
interface nsIAccessiblePivot : nsISupports
{
const TextBoundaryType CHAR_BOUNDARY = 0;
const TextBoundaryType WORD_BOUNDARY = 1;
const TextBoundaryType LINE_BOUNDARY = 2;
const TextBoundaryType ATTRIBUTE_RANGE_BOUNDARY = 3;
/**
* 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 start offset of the text range the pivot points at, otherwise -1.
*/
readonly attribute long startOffset;
/**
* The end offset of the text range the pivot points at, otherwise -1.
*/
readonly attribute long endOffset;
/**
* Set the pivot's text range in a text accessible.
*
* @param aTextAccessible [in] the text accessible that contains the desired
* range.
* @param aStartOffset [in] the start offset to set.
* @param aEndOffset [in] the end offset to set.
* @throws NS_ERROR_INVALID_ARG when the offset exceeds the accessible's
* character count.
*/
void setTextRange(in nsIAccessibleText aTextAccessible,
in long aStartOffset, in long aEndOffset);
/**
* Move pivot to next object complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveNext(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to previous object complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean movePrevious(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to first object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveFirst(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to last object in subtree complying to given traversal rule.
*
* @param aRule [in] traversal rule to use.
* @return true on success, false if there are no new nodes to traverse to.
*/
boolean moveLast(in nsIAccessibleTraversalRule aRule);
/**
* Move pivot to next text range.
*
* @param aBoundary [in] type of boundary for next text range, character, word,
* etc.
* @return true on success, false if there are is no more text.
*/
boolean moveNextByText(in TextBoundaryType aBoundary);
/**
* Move pivot to previous text range.
*
* @param aBoundary [in] type of boundary for previous text range, character,
* word, etc.
* @return true on success, false if there are is no more text.
*/
boolean movePreviousByText(in TextBoundaryType aBoundary);
/**
* 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(b6508c5e-c081-467d-835c-613eedf9ee9b)]
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 aOldStart [in] the old start offset, or -1.
* @param aOldEnd [in] the old end offset, or -1.
*/
void onPivotChanged(in nsIAccessiblePivot aPivot,
in nsIAccessible aOldAccessible,
in long aOldStart, in long aOldEnd);
};
[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)]
interface nsIAccessibleTraversalRule : nsISupports
{
/* Ignore this accessible object */
const unsigned short FILTER_IGNORE = 0x0;
/* Accept this accessible object */
const unsigned short FILTER_MATCH = 0x1;
/* 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;
/**
* 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.
*
* @param aRoles [out] an array of the roles to match.
* @param aCount [out] the length of the array.
*/
void getMatchRoles([array, size_is(aCount)]out unsigned long aRoles,
[retval]out unsigned long aCount);
/**
* Determines if a given accessible is to be accepted in our traversal rule
*
* @param aAccessible [in] accessible to examine.
* @return a bitfield of FILTER_MATCH and FILTER_IGNORE_SUBTREE.
*/
unsigned short match(in nsIAccessible aAccessible);
};

Просмотреть файл

@ -45,7 +45,7 @@ interface nsIPresShell;
interface nsIDOMWindow;
interface nsIAccessNode;
interface nsIDOMDOMStringList;
interface nsIAccessiblePivot;
/**
* An interface for in-process accessibility clients
@ -112,6 +112,14 @@ interface nsIAccessibleRetrieval : nsISupports
* @return cached accessible for the given DOM node if any
*/
nsIAccessible getAccessibleFromCache(in nsIDOMNode aNode);
/**
* Create a new pivot for tracking a position and traversing a subtree.
*
* @param aRoot [in] the accessible root for the pivot
* @return a new pivot
*/
nsIAccessiblePivot createAccessiblePivot(in nsIAccessible aRoot);
};

Просмотреть файл

@ -65,6 +65,7 @@ CPPSRCS = \
nsAccUtils.cpp \
nsAccessibilityService.cpp \
nsAccessible.cpp \
nsAccessiblePivot.cpp \
nsAccTreeWalker.cpp \
nsBaseWidgetAccessible.cpp \
nsEventShell.cpp \

Просмотреть файл

@ -40,6 +40,7 @@
// NOTE: alphabetically ordered
#include "nsAccessibilityService.h"
#include "nsAccessiblePivot.h"
#include "nsCoreUtils.h"
#include "nsAccUtils.h"
#include "nsApplicationAccessibleWrap.h"
@ -864,6 +865,23 @@ nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
return NS_OK;
}
NS_IMETHODIMP
nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
nsIAccessiblePivot** aPivot)
{
NS_ENSURE_ARG_POINTER(aPivot);
NS_ENSURE_ARG(aRoot);
*aPivot = nsnull;
nsRefPtr<nsAccessible> accessibleRoot(do_QueryObject(aRoot));
NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
NS_ADDREF(*aPivot = pivot);
return NS_OK;
}
// nsIAccesibilityService
nsAccessible*
nsAccessibilityService::GetAccessibleInShell(nsINode* aNode,

Просмотреть файл

@ -534,6 +534,7 @@ static const char kEventTypeNames[][40] = {
"hypertext changed", // EVENT_HYPERTEXT_CHANGED
"hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
"object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
"virtual cursor changed" // EVENT_VIRTUALCURSOR_CHANGED
};
/**

Просмотреть файл

@ -0,0 +1,526 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsAccessiblePivot.h"
#include "nsAccessible.h"
#include "nsAccUtils.h"
#include "nsHyperTextAccessible.h"
#include "States.h"
#include "nsArrayUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsISupportsPrimitives.h"
using namespace mozilla::a11y;
/**
* An object that stores a given traversal rule during
*/
class RuleCache
{
public:
RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
mAcceptRoles(nsnull) { }
~RuleCache () {
if (mAcceptRoles)
nsMemory::Free(mAcceptRoles);
}
nsresult ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult);
private:
nsCOMPtr<nsIAccessibleTraversalRule> mRule;
PRUint32* mAcceptRoles;
PRUint32 mAcceptRolesLength;
PRUint32 mPreFilter;
};
////////////////////////////////////////////////////////////////////////////////
// nsAccessiblePivot
nsAccessiblePivot::nsAccessiblePivot(nsAccessible* aRoot) :
mRoot(aRoot), mPosition(nsnull),
mStartOffset(-1), mEndOffset(-1)
{
NS_ASSERTION(aRoot, "A root accessible is required");
}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
PRUint32 i, length = tmp->mObservers.Length(); \
for (i = 0; i < length; ++i) {
cb.NoteXPCOMChild(tmp->mObservers.ElementAt(i).get());
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccessiblePivot)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPosition)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
NS_INTERFACE_MAP_ENTRY(nsAccessiblePivot)
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 = mRoot);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
{
NS_ENSURE_ARG_POINTER(aPosition);
NS_IF_ADDREF(*aPosition = mPosition);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
{
nsRefPtr<nsAccessible> secondPosition;
if (aPosition) {
secondPosition = do_QueryObject(aPosition);
if (!secondPosition || !IsRootDescendant(secondPosition))
return NS_ERROR_INVALID_ARG;
}
// Swap old position with new position, saves us an AddRef/Release.
mPosition.swap(secondPosition);
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = mEndOffset = -1;
NotifyPivotChanged(secondPosition, oldStart, oldEnd);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetStartOffset(PRInt32* aStartOffset)
{
NS_ENSURE_ARG_POINTER(aStartOffset);
*aStartOffset = mStartOffset;
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetEndOffset(PRInt32* aEndOffset)
{
NS_ENSURE_ARG_POINTER(aEndOffset);
*aEndOffset = mEndOffset;
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
PRInt32 aStartOffset, PRInt32 aEndOffset)
{
NS_ENSURE_ARG(aTextAccessible);
// Check that start offset is smaller than end offset, and that if a value is
// smaller than 0, both should be -1.
NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
(aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
NS_ERROR_INVALID_ARG);
nsRefPtr<nsHyperTextAccessible> newPosition = do_QueryObject(aTextAccessible);
if (!newPosition || !IsRootDescendant(newPosition))
return NS_ERROR_INVALID_ARG;
// Make sure the given offsets don't exceed the character count.
PRInt32 charCount = newPosition->CharacterCount();
if (aEndOffset > charCount)
return NS_ERROR_FAILURE;
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = aStartOffset;
mEndOffset = aEndOffset;
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
mPosition = newPosition.forget();
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
return NS_OK;
}
// Traversal functions
NS_IMETHODIMP
nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchForward(mPosition, aRule, false, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchBackward(mPosition, aRule, false, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
nsresult rv = NS_OK;
nsAccessible* accessible = SearchForward(mRoot, aRule, true, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
*aResult = false;
nsresult rv = NS_OK;
nsAccessible* lastAccessible = mRoot;
nsAccessible* accessible = nsnull;
// First got to the last accessible in pre-order
while (lastAccessible->HasChildren())
lastAccessible = lastAccessible->LastChild();
// Search backwards from last accessible and find the last occurrence in the doc
accessible = SearchBackward(lastAccessible, aRule, true, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = accessible;
if (*aResult)
MovePivotInternal(accessible);
return NS_OK;
}
// TODO: Implement
NS_IMETHODIMP
nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
// TODO: Implement
NS_IMETHODIMP
nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
// 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::IsRootDescendant(nsAccessible* aAccessible)
{
nsAccessible* accessible = aAccessible;
do {
if (accessible == mRoot)
return true;
} while ((accessible = accessible->Parent()));
return false;
}
void
nsAccessiblePivot::MovePivotInternal(nsAccessible* aPosition)
{
nsRefPtr<nsAccessible> oldPosition = mPosition.forget();
mPosition = aPosition;
PRInt32 oldStart = mStartOffset, oldEnd = mEndOffset;
mStartOffset = mEndOffset = -1;
NotifyPivotChanged(oldPosition, oldStart, oldEnd);
}
nsAccessible*
nsAccessiblePivot::SearchBackward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv)
{
*rv = NS_OK;
// Initial position could be unset, in that case return null.
if (!aAccessible)
return nsnull;
RuleCache cache(aRule);
nsAccessible* accessible = aAccessible;
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (searchCurrent) {
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
while (accessible != mRoot) {
nsAccessible* parent = accessible->Parent();
PRInt32 idxInParent = accessible->IndexInParent();
while (idxInParent > 0) {
if (!(accessible = parent->GetChildAt(--idxInParent)))
continue;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
nsAccessible* lastChild;
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
(lastChild = accessible->LastChild())) {
parent = accessible;
accessible = lastChild;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
}
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
if (!(accessible = parent))
break;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
return nsnull;
}
nsAccessible*
nsAccessiblePivot::SearchForward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv)
{
*rv = NS_OK;
// Initial position could be not set, in that case begin search from root.
nsAccessible *accessible = (!aAccessible) ? mRoot.get() : aAccessible;
RuleCache cache(aRule);
PRUint16 filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (searchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
return accessible;
while (true) {
nsAccessible* firstChild = nsnull;
while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
(firstChild = accessible->FirstChild())) {
accessible = firstChild;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
nsAccessible* sibling = nsnull;
nsAccessible* temp = accessible;
do {
if (temp == mRoot)
break;
sibling = temp->NextSibling();
if (sibling)
break;
} while ((temp = temp->Parent()));
if (!sibling)
break;
accessible = sibling;
*rv = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*rv, nsnull);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
}
return nsnull;
}
void
nsAccessiblePivot::NotifyPivotChanged(nsAccessible* aOldPosition,
PRInt32 aOldStart, PRInt32 aOldEnd)
{
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
nsIAccessiblePivotObserver* obs = iter.GetNext();
obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd);
}
}
nsresult
RuleCache::ApplyFilter(nsAccessible* aAccessible, PRUint16* aResult)
{
*aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (!mAcceptRoles) {
nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength);
NS_ENSURE_SUCCESS(rv, rv);
rv = mRule->GetPreFilter(&mPreFilter);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mPreFilter) {
PRUint64 state = aAccessible->State();
if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
(state & states::INVISIBLE))
return NS_OK;
if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
(state & states::OFFSCREEN))
return NS_OK;
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
!(state & states::FOCUSABLE))
return NS_OK;
}
if (mAcceptRolesLength > 0) {
PRUint32 accessibleRole = aAccessible->Role();
bool matchesRole = false;
for (PRUint32 idx = 0; idx < mAcceptRolesLength; idx++) {
matchesRole = mAcceptRoles[idx] == accessibleRole;
if (matchesRole)
break;
}
if (!matchesRole)
return NS_OK;
}
return mRule->Match(aAccessible, aResult);
}

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Eitan Isaacson <eitan@monotonous.org> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _nsAccessiblePivot_H_
#define _nsAccessiblePivot_H_
#include "nsIAccessiblePivot.h"
#include "nsAutoPtr.h"
#include "nsTObserverArray.h"
#include "nsCycleCollectionParticipant.h"
class nsAccessible;
class nsIAccessibleTraversalRule;
/**
* Class represents an accessible pivot.
*/
class nsAccessiblePivot: public nsIAccessiblePivot
{
public:
nsAccessiblePivot(nsAccessible* 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.
*/
nsAccessible* Position() { return mPosition; }
private:
nsAccessiblePivot() MOZ_DELETE;
nsAccessiblePivot(const nsAccessiblePivot&) MOZ_DELETE;
void operator = (const nsAccessiblePivot&) MOZ_DELETE;
/*
* Notify all observers on a pivot change.
*/
void NotifyPivotChanged(nsAccessible* aOldAccessible,
PRInt32 aOldStart, PRInt32 aOldEnd);
/*
* Check to see that the given accessible is in the pivot's subtree.
*/
bool IsRootDescendant(nsAccessible* aAccessible);
/*
* Search in preorder for the first accessible to match the rule.
*/
nsAccessible* SearchForward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv);
/*
* Reverse search in preorder for the first accessible to match the rule.
*/
nsAccessible* SearchBackward(nsAccessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
bool searchCurrent,
nsresult* rv);
/*
* Update the pivot, and notify observers.
*/
void MovePivotInternal(nsAccessible* aPosition);
/*
* The root accessible.
*/
nsRefPtr<nsAccessible> mRoot;
/*
* The current pivot position.
*/
nsRefPtr<nsAccessible> mPosition;
/*
* The text start offset ofthe pivot.
*/
PRInt32 mStartOffset;
/*
* The text end offset ofthe pivot.
*/
PRInt32 mEndOffset;
/*
* The list of pivot-changed observers.
*/
nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> > mObservers;
};
#endif

Просмотреть файл

@ -39,6 +39,7 @@
#include "AccIterator.h"
#include "nsAccCache.h"
#include "nsAccessibilityService.h"
#include "nsAccessiblePivot.h"
#include "nsAccTreeWalker.h"
#include "nsAccUtils.h"
#include "nsRootAccessible.h"
@ -105,7 +106,8 @@ nsDocAccessible::
nsIWeakReference *aShell) :
nsHyperTextAccessibleWrap(aRootContent, aShell),
mDocument(aDocument), mScrollPositionChangedTicks(0),
mLoadState(eTreeConstructionPending), mLoadEventType(0)
mLoadState(eTreeConstructionPending), mLoadEventType(0),
mVirtualCursor(nsnull)
{
mFlags |= eDocAccessible;
@ -125,6 +127,10 @@ nsDocAccessible::
// nsAccDocManager creates document accessible when scrollable frame is
// available already, it should be safe time to add scroll listener.
AddScrollListener();
// We provide a virtual cursor if this is a root doc or if it's a tab doc.
mIsCursorable = (!(mDocument->GetParentDocument()) ||
nsCoreUtils::IsTabDocument(mDocument));
}
nsDocAccessible::~nsDocAccessible()
@ -142,6 +148,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
NotificationController)
if (tmp->mVirtualCursor) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
nsAccessiblePivot)
}
PRUint32 i, length = tmp->mChildDocuments.Length();
for (i = 0; i < length; ++i) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
@ -154,6 +165,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
tmp->mDependentIDsHash.Clear();
tmp->mNodeToAccessibleMap.Clear();
@ -167,7 +179,10 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
mIsCursorable)
foundInterface = 0;
nsresult status;
@ -516,6 +531,27 @@ nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex,
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
}
// nsIAccessibleVirtualCursor method
NS_IMETHODIMP
nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
{
NS_ENSURE_ARG_POINTER(aVirtualCursor);
*aVirtualCursor = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
if (!mVirtualCursor) {
mVirtualCursor = new nsAccessiblePivot(this);
mVirtualCursor->AddObserver(this);
}
NS_ADDREF(*aVirtualCursor = mVirtualCursor);
return NS_OK;
}
// nsIAccessibleHyperText method
NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
{
@ -637,6 +673,11 @@ nsDocAccessible::Shutdown()
mChildDocuments.Clear();
if (mVirtualCursor) {
mVirtualCursor->RemoveObserver(this);
mVirtualCursor = nsnull;
}
mWeakShell = nsnull; // Avoid reentrancy
mDependentIDsHash.Clear();
@ -888,6 +929,20 @@ NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessiblePivotObserver
NS_IMETHODIMP
nsDocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
nsIAccessible* aOldAccessible,
PRInt32 aOldStart, PRInt32 aOldEnd)
{
nsRefPtr<AccEvent> event = new AccEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, this);
nsEventShell::FireEvent(event);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIDocumentObserver

Просмотреть файл

@ -39,7 +39,9 @@
#ifndef _nsDocAccessible_H_
#define _nsDocAccessible_H_
#include "nsIAccessibleCursorable.h"
#include "nsIAccessibleDocument.h"
#include "nsIAccessiblePivot.h"
#include "nsEventShell.h"
#include "nsHyperTextAccessibleWrap.h"
@ -58,6 +60,7 @@
#include "nsIDocShellTreeNode.h"
class nsIScrollableView;
class nsAccessiblePivot;
const PRUint32 kDefaultCacheSize = 256;
@ -74,8 +77,10 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
public nsIDocumentObserver,
public nsIObserver,
public nsIScrollPositionListener,
public nsSupportsWeakReference
{
public nsSupportsWeakReference,
public nsIAccessibleCursorable,
public nsIAccessiblePivotObserver
{
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDocAccessible, nsAccessible)
@ -84,6 +89,10 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
NS_DECL_NSIOBSERVER
NS_DECL_NSIACCESSIBLECURSORABLE
NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
public:
using nsAccessible::GetParent;
@ -595,6 +604,16 @@ protected:
nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
/**
* Whether we support nsIAccessibleCursorable, used when querying the interface.
*/
bool mIsCursorable;
/**
* The virtual cursor of the document when it supports nsIAccessibleCursorable.
*/
nsRefPtr<nsAccessiblePivot> mVirtualCursor;
/**
* A storage class for pairing content with one of its relation attributes.
*/

Просмотреть файл

@ -131,6 +131,7 @@ static const PRUint32 gWinEventMap[] = {
IA2_EVENT_HYPERTEXT_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
IA2_EVENT_HYPERTEXT_NLINKS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
kEVENT_LAST_ENTRY // nsIAccessibleEvent::EVENT_LAST_ENTRY
};

Просмотреть файл

@ -52,6 +52,7 @@ DIRS = \
hyperlink \
hypertext \
name \
pivot \
relations \
selectable \
states \
@ -81,6 +82,7 @@ _TEST_FILES =\
grid.js \
layout.js \
name.js \
pivot.js \
relations.js \
role.js \
selectable.js \

Просмотреть файл

@ -30,10 +30,13 @@ const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableTex
const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
const nsIAccessibleCursorable = Components.interfaces.nsIAccessibleCursorable;
const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot;
const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable;
const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule;
const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue;
const nsIObserverService = Components.interfaces.nsIObserverService;

Просмотреть файл

@ -27,6 +27,7 @@ const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED;
const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
const EVENT_VIRTUALCURSOR_CHANGED = nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED;
////////////////////////////////////////////////////////////////////////////////
// General

Просмотреть файл

@ -0,0 +1,217 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
////////////////////////////////////////////////////////////////////////////////
// Constants
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
////////////////////////////////////////////////////////////////////////////////
// Traversal rules
/**
* Rule object to traverse all focusable nodes and text nodes.
*/
var HeadersTraversalRule =
{
getMatchRoles: function(aRules)
{
aRules.value = [ROLE_HEADING];
return aRules.value.length;
},
preFilter: PREFILTER_INVISIBLE,
match: function(aAccessible)
{
return FILTER_MATCH;
},
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
}
/**
* Traversal rule for all focusable nodes or leafs.
*/
var ObjectTraversalRule =
{
getMatchRoles: function(aRules)
{
aRules.value = [];
return 0;
},
preFilter: PREFILTER_INVISIBLE,
match: function(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_STATICTEXT && aAccessible.name.trim())
rv = FILTER_MATCH;
return rv;
},
QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
};
////////////////////////////////////////////////////////////////////////////////
// Virtual state invokers and checkers
/**
* A checker for virtual cursor changed events.
*/
function virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets)
{
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
this.check = function virtualCursorChangedChecker_check(aEvent)
{
var position = aDocAcc.virtualCursor.position;
position.QueryInterface(nsIAccessNode);
var idMatches = position.DOMNode.id == aIdOrNameOrAcc;
var nameMatches = position.name == aIdOrNameOrAcc;
var accMatches = position == aIdOrNameOrAcc;
SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
"expecting " + aIdOrNameOrAcc + ", got '" +
prettyName(position));
if (aTextOffsets) {
SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
"wrong start offset");
SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
"wrong end offset");
}
};
}
/**
* Set a text range in the pivot and wait for virtual cursor change event.
*
* @param aDocAcc document that manages the virtual cursor
* @param aTextAccessible accessible to set to virtual cursor's position
* @param aTextOffsets start and end offsets of text range to set in virtual
* cursor
*/
function setVirtualCursorRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
{
this.invoke = function virtualCursorChangedInvoker_invoke()
{
SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
aDocAcc.virtualCursor.setTextRange(aTextAccessible,
aTextOffsets[0],
aTextOffsets[1]);
};
this.getID = function setVirtualCursorRangeInvoker_getID()
{
return "Set offset in " + prettyName(aTextAccessible) +
" to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
}
this.eventSeq = [
new virtualCursorChangedChecker(aDocAcc, aTextAccessible, aTextOffsets)
];
}
/**
* Move the pivot and wait for virtual cursor change event.
*
* @param aDocAcc document that manages the virtual cursor
* @param aPivotMoveMethod method to test (ie. "moveNext", "moveFirst", etc.)
* @param aRule traversal rule object
* @param aIdOrNameOrAcc id, accessivle or accessible name to expect virtual
* cursor to land on after performing move method.
*/
function setVirtualCursorPosInvoker(aDocAcc, aPivotMoveMethod, aRule,
aIdOrNameOrAcc)
{
this.invoke = function virtualCursorChangedInvoker_invoke()
{
var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
SimpleTest.ok((aIdOrNameOrAcc && moved) || (!aIdOrNameOrAcc && !moved),
"moved pivot");
};
this.getID = function setVirtualCursorPosInvoker_getID()
{
return "Do " + (aIdOrNameOrAcc ? "" : "no-op ") + aPivotMoveMethod;
}
if (aIdOrNameOrAcc) {
this.eventSeq = [ new virtualCursorChangedChecker(aDocAcc, aIdOrNameOrAcc) ];
} else {
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 event queue in which to push invoker sequence.
* @param aDocAcc the managing document of the virtual cursor we are testing
* @param aRule the traversal rule to use in the invokers
* @param aSequence a sequence of accessible names or elemnt ids to expect with
* the given rule in the given document
*/
function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
{
aDocAcc.virtualCursor.position = null;
for (var i = 0; i < aSequence.length; i++) {
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "moveNext",
aRule, aSequence[i]);
aQueue.push(invoker);
}
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
for (var i = aSequence.length-2; i >= 0; i--) {
var invoker = new setVirtualCursorPosInvoker(aDocAcc, "movePrevious",
aRule, aSequence[i])
aQueue.push(invoker);
}
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
aQueue.push(new setVirtualCursorPosInvoker(
aDocAcc, "moveLast", aRule, aSequence[aSequence.length - 1]));
// No further more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "moveNext", aRule, null));
aQueue.push(new setVirtualCursorPosInvoker(
aDocAcc, "moveFirst", aRule, aSequence[0]));
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVirtualCursorPosInvoker(aDocAcc, "movePrevious", aRule, null));
}
/**
* A debug utility for writing proper sequences for queueTraversalSequence.
*/
function dumpTraversalSequence(aPivot, aRule)
{
var sequence = []
if (aPivot.moveFirst(aRule)) {
do {
sequence.push("'" + prettyName(aPivot.position) + "'");
} while (aPivot.moveNext(aRule))
}
SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
}

Просмотреть файл

@ -0,0 +1,54 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Eitan Isaacson <eitan@monotonous.org> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = accessible/pivot
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
doc_virtualcursor.html \
test_virtualcursor.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)

Просмотреть файл

@ -0,0 +1,26 @@
<!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">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">Second Section Title</h2>
<p id="paragraph-2">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<iframe
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
</iframe>
<p>
<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>
</body>
</html>

Просмотреть файл

@ -0,0 +1,95 @@
<!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 type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.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">
var gBrowserWnd = null;
var gQueue = null;
function doTest()
{
var rootAcc = getRootAccessible(browserWindow().document);
try {
rootAcc.QueryInterface(nsIAccessibleCursorable);
} catch (e) {
ok(false, "Root accessible does not support nsIAccessibleCursorable");
}
var doc = currentTabDocument();
var docAcc = getAccessible(doc, [nsIAccessibleDocument,
nsIAccessibleCursorable]);
// Test that embedded documents don't have their own virtual cursor.
is(docAcc.childDocumentCount, 1, "Expecting one child document");
var childDoc = docAcc.getChildDocumentAt(0);
var supportsVC = true;
try {
childDoc.QueryInterface(nsIAccessibleCursorable);
} catch (e) {
supportsVC = false;
}
ok(!supportsVC, "no nsIAccessibleCursorable support in child document");
gQueue = new eventQueue();
gQueue.onFinish = function onFinish()
{
closeBrowserWindow();
}
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule,
['heading-1-1', 'heading-2-1', 'heading-2-2']);
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule,
['Main Title', 'First Section 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.', 'Link 1', 'Link 2', 'Link 3']);
// Just a random smoke test to see if our setTextRange works.
gQueue.push(
new setVirtualCursorRangeInvoker(
docAcc,
getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText),
[2,6]));
gQueue.invoke();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
/* We open a new browser because we need to test with a top-level content
document. */
openBrowserWindow(
doTest,
"chrome://mochitests/content/a11y/accessible/pivot/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>