Accessibility/SoftFocus
Background
It is useful to have a focus-like state that is exclusive to accessibility and is independant of the DOM state. From the user's perspective this allows reviewing a document or web application without directly affecting its state, like moving the caret or cycling through focusable elements. This is useful in mobile, for example a user could navigate to a text entry without having it capture input. Only if the user chooses to enter text they would "activate" the text entry which would bring up an on screen keyboard, etc. This feature would also allow easier navigation of content.
Interfaces
nsIAccessibleVirtualPivot
interface nsIAccessiblePivot : nsISupports
{
/*
* The accessible the pivot is currently pointed at.
*/
readonly attribute nsIAccessible accessible;
/*
* The document that owns this pivot.
*/
readonly attribute nsIAccessibleDocument rootAccessible;
/*
* The start offset in the accessible's text. Only supported when the accessible has
* the nsIAccessibleText interface. If no explicit offset was set or if it is not
* supported in the pivot's accessible this is -1.
*/
readonly attribute long startOffset;
/*
* The end offset in the accessible's text. Only supported when the accessible has
* the nsIAccessibleText interface. If no explicit offset was set or if it is not
* supported in the pivot's accessible this is -1.
*/
readonly attribute long endOffset;
/*
* Set the accessible this pivot should point to.
*
* @param aAccessible the new accessible to point to.
* @throws NS_ERROR_INVALID_ARG when given accessible is not in the pivot's document.
*/
void setAccessible(in nsIAccessible aAccessible);
/**
* Set the pivot's text range in a text accessible.
*
* @param aStartOffset the start offset to set.
* @param aEndOffset the end offset to set.
* @throws NS_ERROR_FAILURE when the pivot's accessible does not have the
* nsIAccessibleText inteface.
*/
void setTextOffset(in long aStartOffset, in long aEndOffset);
/*
* Add an observer for pivot changes.
*
* @param aObserver the observer object to be notified of pivot changes.
*/
void addObserver(in nsIAccessiblePivotObserver aObserver);
/*
* Remove an observer for pivot changes.
*
* @param aObserver the observer object to remove from being notified.
*/
void removeObserver(in nsIAccessiblePivotObserver aObserver);
};
nsIAccessibleVirtualPivotObserver
interface nsIAccessibleVirtualPivotObserver : nsISupports
{
/**
* Called when accessible of pivot changes.
*/
void onAccessibleChanged(in nsIAccessible aOldAccessible, in nsIAccessible aNewAccessible);
/**
* Called when text offset changes.
*/
void onTextOffsetChanged(in long aOldStart, in long aOldEnd,
in long aNewStart, in long aNewEnd);
}
nsIAccessibleDocument
interface nsIAccessibleDocument : nsISupports
{
...
/**
* The virtual cursor pivot for this document.
*/
readonly attribute nsIAccessibleVirtualPivot virtualCursor;
/**
* Create a new pivot in this document.
*/
nsIAccessibleVirtualPivot createVirtualPivot();
}
Events
When the document's virtual cursor pivot changes, an accessibility event of type EVENT_VIRTUAL_CURSOR_CHANGED is fired that supports the following interface:
nsIAccessibleVirtualCursorChangedEvent
interface nsIAccessibleVirtualCursorChangedEvent : nsISupports
{
/**
* The previous accessible the cursor pointed at or null.
*/
readonly attribute nsIAccessible previousAccessible;
/**
* The previous start offset of the cursor or -1.
*/
readonly attribute long previousStartOffset;
/**
* The previous end offset of the cursor or -1.
*/
readonly attribute long previousEndOffset;
}
Example Usage
Setting Virtual Cursor
An input event handler that changes the virtual cursor.
function inputEventHandler(event) {
...
treeWalker.currentNode = docAcc.virtualCursor.accessible;
if (treeWalker.next())
docAcc.virtualCursor.setAccessible(treeWalker.currentNode);
}
Presenting Virtual Cursor
An event listener that draws a ring for the virtual cursor. drawRect() is left to your imagination.
function handleEvent(aEvent) {
if (aEvent.type != EVENT_VIRTUAL_CURSOR_CHANGED)
return;
let pivotEvent = aEvent.QueryInterface(nsIAccessibleVirtualPivotChangedEvent);
showVirtualCursorRing(pivotEvent.accessible,
pivotEvent.startOffset,
pivotEvent.endOffset);
}
function showVirtualCursorRing (aAccessible, aStartOffset, aEndOffset) {
let x = {};
let y = {};
let w = {};
let h = {};
if (aStartOffset >= 0 && aEndOffset >= 0) {
try {
let textAcc = aAccessible.QueryInterface(nsIAccessibleText);
let start = {};
let end = {};
textAcc.getRangeExtents(aStartOffset, aEndOffset, x, y, w, h,
COORDTYPE_SCREEN_RELATIVE);
drawRect(x.value, y.value, w.value, h.value);
return;
} catch (e) {
}
}
aAccessible.getBounds(x, y, w, h);
drawRect(x.value, y.value, w.value, h.value);
}